Mana_Filters - Version 13.10.17.19

Version Notes

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

Download this release

Release Info

Developer Mana Team
Extension Mana_Filters
Version 13.10.17.19
Comparing to
See all releases


Code changes from version 12.12.21.19 to 13.10.17.19

Files changed (154) hide show
  1. app/code/local/Mana/Core/Block/Js.php +18 -2
  2. app/code/local/Mana/Core/Block/Require.php +21 -0
  3. app/code/local/Mana/Core/Helper/Data.php +293 -1
  4. app/code/local/Mana/Core/Helper/Debug.php +37 -0
  5. app/code/local/Mana/Core/Helper/Files.php +36 -6
  6. app/code/local/Mana/Core/Helper/Js.php +469 -1
  7. app/code/local/Mana/Core/Helper/Json.php +22 -0
  8. app/code/local/Mana/Core/Helper/Layer.php +89 -0
  9. app/code/local/Mana/Core/Helper/Layout.php +14 -0
  10. app/code/local/Mana/Core/Helper/Logger.php +110 -0
  11. app/code/local/Mana/Core/Helper/Mbstring.php +69 -0
  12. app/code/local/Mana/Core/Helper/PageType.php +24 -0
  13. app/code/local/Mana/Core/Helper/PageType/Category.php +24 -0
  14. app/code/local/Mana/Core/Helper/PageType/CmsPage.php +20 -0
  15. app/code/local/Mana/Core/Helper/PageType/HomePage.php +20 -0
  16. app/code/local/Mana/Core/Helper/PageType/Search.php +20 -0
  17. app/code/local/Mana/Core/Helper/Router.php +91 -0
  18. app/code/local/Mana/Core/Helper/Seo.php +72 -0
  19. app/code/local/Mana/Core/Helper/UrlTemplate.php +21 -0
  20. app/code/local/Mana/Core/Helper/Utils.php +118 -0
  21. app/code/local/Mana/Core/Model/Callback.php +6 -6
  22. app/code/local/Mana/Core/Model/Closure.php +18 -0
  23. app/code/local/Mana/Core/Model/Indexer.php +62 -0
  24. app/code/local/Mana/Core/Model/Observer.php +122 -1
  25. app/code/local/Mana/Core/Model/Source/Js.php +27 -0
  26. app/code/local/Mana/Core/Model/Source/YesNoDefault.php +22 -0
  27. app/code/local/Mana/Core/Resource/Indexer.php +19 -0
  28. app/code/local/Mana/Core/Rewrite/PageCache/Processor.php +27 -0
  29. app/code/local/Mana/Core/etc/adminhtml.xml +64 -0
  30. app/code/local/Mana/Core/etc/config.xml +109 -9
  31. app/code/local/Mana/Core/etc/system.xml +57 -0
  32. app/code/local/Mana/Db/Exception/Formula.php +16 -0
  33. app/code/local/Mana/Db/Exception/Validation.php +15 -2
  34. app/code/local/Mana/Db/Helper/Config.php +414 -0
  35. app/code/local/Mana/Db/Helper/Data.php +304 -86
  36. app/code/local/Mana/Db/Helper/Formula.php +471 -0
  37. app/code/local/Mana/Db/Helper/Formula/Abstract.php +73 -0
  38. app/code/local/Mana/Db/Helper/Formula/DependencyFinder.php +100 -0
  39. app/code/local/Mana/Db/Helper/Formula/Entity.php +45 -0
  40. app/code/local/Mana/Db/Helper/Formula/Entity/Aggregate.php +87 -0
  41. app/code/local/Mana/Db/Helper/Formula/Entity/Aggregate/Field.php +60 -0
  42. app/code/local/Mana/Db/Helper/Formula/Entity/Foreign.php +50 -0
  43. app/code/local/Mana/Db/Helper/Formula/Entity/Frontend.php +43 -0
  44. app/code/local/Mana/Db/Helper/Formula/Entity/Normal.php +36 -0
  45. app/code/local/Mana/Db/Helper/Formula/Evaluator.php +20 -0
  46. app/code/local/Mana/Db/Helper/Formula/Function.php +28 -0
  47. app/code/local/Mana/Db/Helper/Formula/Function/Count.php +47 -0
  48. app/code/local/Mana/Db/Helper/Formula/Function/Glue.php +83 -0
  49. app/code/local/Mana/Db/Helper/Formula/Function/If.php +51 -0
  50. app/code/local/Mana/Db/Helper/Formula/Function/Lower.php +30 -0
  51. app/code/local/Mana/Db/Helper/Formula/Function/Seo.php +33 -0
  52. app/code/local/Mana/Db/Helper/Formula/Function/SkinUrl.php +32 -0
  53. app/code/local/Mana/Db/Helper/Formula/Parser.php +379 -0
  54. app/code/local/Mana/Db/Helper/Formula/Processor.php +63 -0
  55. app/code/local/Mana/Db/Helper/Formula/Processor/Eav.php +41 -0
  56. app/code/local/Mana/Db/Helper/Formula/Processor/Entity.php +178 -0
  57. app/code/local/Mana/Db/Helper/Formula/Processor/System.php +16 -0
  58. app/code/local/Mana/Db/Helper/Formula/Processor/Table.php +86 -0
  59. app/code/local/Mana/Db/Helper/Formula/Selector.php +450 -0
  60. app/code/local/Mana/Db/Model/Entity.php +296 -0
  61. app/code/local/Mana/Db/Model/Entity/Indexer.php +349 -0
  62. app/code/local/Mana/Db/Model/Formula/Alias.php +58 -0
  63. app/code/local/Mana/Db/Model/Formula/Alias/Array.php +104 -0
  64. app/code/local/Mana/Db/Model/Formula/Alias/Empty.php +68 -0
  65. app/code/local/Mana/Db/Model/Formula/Alias/Single.php +86 -0
  66. app/code/local/Mana/Db/Model/Formula/Closure.php +16 -0
  67. app/code/local/Mana/Db/Model/Formula/Closure/AggregateFieldJoin.php +50 -0
  68. app/code/local/Mana/Db/Model/Formula/Closure/ForeignJoin.php +28 -0
  69. app/code/local/Mana/Db/Model/Formula/Closure/ForeignJoinEnd.php +38 -0
  70. app/code/local/Mana/Db/Model/Formula/Closure/Join.php +44 -0
  71. app/code/local/Mana/Db/Model/Formula/Context.php +217 -0
  72. app/code/local/Mana/Db/Model/Formula/Entity.php +53 -0
  73. app/code/local/Mana/Db/Model/Formula/Expr.php +26 -0
  74. app/code/local/Mana/Db/Model/Formula/Field.php +37 -0
  75. app/code/local/Mana/Db/Model/Formula/Node.php +17 -0
  76. app/code/local/Mana/Db/Model/Formula/Node/Add.php +28 -0
  77. app/code/local/Mana/Db/Model/Formula/Node/Field.php +21 -0
  78. app/code/local/Mana/Db/Model/Formula/Node/FormulaExpr.php +17 -0
  79. app/code/local/Mana/Db/Model/Formula/Node/FunctionCall.php +21 -0
  80. app/code/local/Mana/Db/Model/Formula/Node/Identifier.php +17 -0
  81. app/code/local/Mana/Db/Model/Formula/Node/Multiply.php +28 -0
  82. app/code/local/Mana/Db/Model/Formula/Node/NullValue.php +13 -0
  83. app/code/local/Mana/Db/Model/Formula/Node/NumberConstant.php +17 -0
  84. app/code/local/Mana/Db/Model/Formula/Node/StringConstant.php +17 -0
  85. app/code/local/Mana/Db/Model/Indexer.php +1 -1
  86. app/code/local/Mana/Db/Model/Observer.php +33 -6
  87. app/code/local/Mana/Db/Model/Setup.php +84 -0
  88. app/code/local/Mana/Db/Model/Setup/Abstract.php +33 -0
  89. app/code/local/Mana/Db/Model/Setup/V13012122.php +478 -0
  90. app/code/local/Mana/Db/Resource/Edit/Session.php +11 -0
  91. app/code/local/Mana/Db/Resource/Entity.php +86 -0
  92. app/code/local/Mana/Db/Resource/Entity/Collection.php +124 -0
  93. app/code/local/Mana/Db/Resource/Entity/Indexer.php +89 -0
  94. app/code/local/Mana/Db/Resource/Entity/JsonCollection.php +186 -0
  95. app/code/local/Mana/Db/Resource/Formula.php +50 -0
  96. app/code/local/Mana/Db/etc/config.xml +8 -2
  97. app/code/local/Mana/Db/etc/m_db.xml +44 -0
  98. app/code/local/Mana/Filters/Block/Filter.php +50 -2
  99. app/code/local/Mana/Filters/Block/View.php +31 -7
  100. app/code/local/Mana/Filters/Helper/Data.php +44 -60
  101. app/code/local/Mana/Filters/Model/Filter/Attribute.php +8 -5
  102. app/code/local/Mana/Filters/Model/Filter/Category.php +28 -1
  103. app/code/local/Mana/Filters/Model/Filter/Decimal.php +24 -18
  104. app/code/local/Mana/Filters/Model/Filter/Price.php +77 -63
  105. app/code/local/Mana/Filters/Model/Filter2.php +25 -0
  106. app/code/local/Mana/Filters/Model/Filter2/Store.php +4 -1
  107. app/code/local/Mana/Filters/Model/Filter2/Value.php +11 -0
  108. app/code/local/Mana/Filters/Model/Item.php +38 -8
  109. app/code/local/Mana/Filters/Model/Observer.php +45 -1
  110. app/code/local/Mana/Filters/Model/Query.php +1 -0
  111. app/code/local/Mana/Filters/Model/Solr/Attribute.php +42 -8
  112. app/code/local/Mana/Filters/Model/Solr/Category.php +18 -3
  113. app/code/local/Mana/Filters/Model/Solr/Decimal.php +8 -6
  114. app/code/local/Mana/Filters/Model/Solr/Price.php +22 -13
  115. app/code/local/Mana/Filters/Model/Solr/Reverse/Attribute.php +1 -2
  116. app/code/local/Mana/Filters/Model/Solr/Reverse/Decimal.php +7 -5
  117. app/code/local/Mana/Filters/Model/Solr/Reverse/Price.php +11 -9
  118. app/code/local/Mana/Filters/Resource/Filter/Decimal.php +19 -17
  119. app/code/local/Mana/Filters/Resource/Filter/Price.php +70 -23
  120. app/code/local/Mana/Filters/Resource/Filter/Reverse/Decimal.php +18 -16
  121. app/code/local/Mana/Filters/Resource/Filter/Reverse/Price.php +13 -8
  122. app/code/local/Mana/Filters/Resource/Filter2.php +13 -0
  123. app/code/local/Mana/Filters/Resource/Filter2/Store.php +6 -0
  124. app/code/local/Mana/Filters/Resource/Filter2/Value.php +24 -0
  125. app/code/local/Mana/Filters/Resource/Filter2/Value/Store.php +5 -1
  126. app/code/local/Mana/Filters/Resource/Indexer/Source.php +3 -0
  127. app/code/local/Mana/Filters/Resource/Solr/And/Attribute.php +1 -3
  128. app/code/local/Mana/Filters/Resource/Solr/Attribute.php +3 -7
  129. app/code/local/Mana/Filters/Resource/Solr/Collection.php +62 -0
  130. app/code/local/Mana/Filters/Resource/Solr/Engine.php +24 -0
  131. app/code/local/Mana/Filters/Resource/Solr/Reverse/Attribute.php +1 -3
  132. app/code/local/Mana/Filters/etc/config.xml +31 -8
  133. app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-13.09.11.14-13.09.23.17.php +29 -0
  134. app/design/adminhtml/default/default/layout/mana_core.xml +5 -2
  135. app/design/adminhtml/default/default/template/mana/core/js.phtml +9 -0
  136. app/design/adminhtml/default/default/template/mana/core/require.phtml +14 -0
  137. app/design/frontend/base/default/layout/mana_core.xml +5 -2
  138. app/design/frontend/base/default/template/mana/core/js.phtml +9 -0
  139. app/design/frontend/base/default/template/mana/core/require.phtml +14 -0
  140. app/design/frontend/base/default/template/mana/filters/cms.phtml +21 -1
  141. app/design/frontend/base/default/template/mana/filters/items/list.phtml +4 -4
  142. app/design/frontend/base/default/template/mana/filters/items/list_popup.phtml +10 -8
  143. app/design/frontend/base/default/template/mana/filters/items/standard.phtml +4 -4
  144. app/design/frontend/base/default/template/mana/filters/items/standard_popup.phtml +10 -5
  145. app/design/frontend/base/default/template/mana/filters/state.phtml +1 -0
  146. app/etc/modules/Mana_Core.xml +6 -0
  147. app/locale/en_US/Mana_Core.csv +16 -0
  148. app/locale/en_US/Mana_Db.csv +13 -1
  149. app/locale/en_US/Mana_Filters.csv +4 -2
  150. js/jquery/froogaloop.js +290 -0
  151. js/jquery/froogaloop.min.js +5 -0
  152. js/jquery/history.js +8 -3
  153. js/jquery/jquery-1.7.2.min.js +0 -6
  154. js/jquery/jquery-ui.js +5960 -5485
app/code/local/Mana/Core/Block/Js.php CHANGED
@@ -12,6 +12,15 @@
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
@@ -59,12 +68,19 @@ class Mana_Core_Block_Js extends Mage_Core_Block_Template {
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
  }
12
  *
13
  */
14
  class Mana_Core_Block_Js extends Mage_Core_Block_Template {
15
+ protected $_config = array();
16
+
17
+ public function setConfig($key, $value) {
18
+ $this->_config[$key] = $value;
19
+ return $this;
20
+ }
21
+ public function getConfig() {
22
+ return $this->_config;
23
+ }
24
  /**
25
  * Contains all the translations registered to be passed to client-side scripts
26
  * @var array | null
68
  }
69
  /**
70
  * Returns all the translations registered to be passed to client-side scripts
71
+ * @return array | null
72
  */
73
  public function getTranslations() { return $this->_translations; }
74
  /**
75
  * Returns all key-value pair arrays registered to be passed to client-side scripts
76
+ * @return array | null
77
  */
78
  public function getOptions() { return $this->_options; }
79
+
80
+ protected function _prepareLayout() {
81
+ $this
82
+ ->setConfig('url.base', Mage::getUrl('', array('_nosid' => true)))
83
+ ->setConfig('url.secureBase', Mage::getUrl('', array('_secure' => true, '_nosid' => true)));
84
+ return $this;
85
+ }
86
  }
app/code/local/Mana/Core/Block/Require.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://www.manadev.com/license Proprietary License
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Core_Block_Require extends Mage_Core_Block_Template {
13
+ protected function _construct() {
14
+ parent::_construct();
15
+ $this->setTemplate('mana/core/require.phtml');
16
+ }
17
+
18
+ public function getBaseRequireUrl() {
19
+ return Mage::getBaseUrl('js').'m-classes';
20
+ }
21
+ }
app/code/local/Mana/Core/Helper/Data.php CHANGED
@@ -11,6 +11,8 @@
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).
@@ -345,7 +347,7 @@ class Mana_Core_Helper_Data extends Mage_Core_Helper_Abstract {
345
  }
346
  return $val;
347
  }
348
- public function jsonForceObjectAndEncode($data) {
349
  return json_encode($this->_forceObjectRecursively($data));
350
  }
351
  protected function _forceObjectRecursively($data) {
@@ -403,6 +405,16 @@ class Mana_Core_Helper_Data extends Mage_Core_Helper_Abstract {
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());
@@ -517,4 +529,284 @@ class Mana_Core_Helper_Data extends Mage_Core_Helper_Abstract {
517
  return null;
518
  }
519
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  }
11
  * @author Mana Team
12
  */
13
  class Mana_Core_Helper_Data extends Mage_Core_Helper_Abstract {
14
+ protected $_pageTypes;
15
+
16
  /**
17
  * Retrieve config value for store by path. By default uses standard Magento function to query core_config_data
18
  * table and use config.xml for default value. Though this could be replaced by extensions (in later versions).
347
  }
348
  return $val;
349
  }
350
+ public function jsonForceObjectAndEncode($data) {
351
  return json_encode($this->_forceObjectRecursively($data));
352
  }
353
  protected function _forceObjectRecursively($data) {
405
  return $request->getRouteName() . '/' . $request->getControllerName() . '/' . $request->getActionName();
406
  }
407
  }
408
+ public function getRouteParams() {
409
+ $request = Mage::app()->getRequest();
410
+
411
+ $result = '';
412
+ foreach ($request->getUserParams() as $key => $value) {
413
+ $result .= '/'.$key.'/'.$value;
414
+ }
415
+ return $result;
416
+ }
417
+
418
  public function isMageVersionEqualOrGreater($version) {
419
  $version = explode('.', $version);
420
  $mageVersion = array_values(Mage::getVersionInfo());
529
  return null;
530
  }
531
  }
532
+
533
+ public function base64EncodeUrl($url) {
534
+ return base64_encode(Mage::getSingleton('core/url')->sessionUrlVar($url));
535
+ }
536
+
537
+ public function inAdmin() {
538
+ return $this->getRequestModule(Mage::app()->getRequest()) ==
539
+ ((string)Mage::getConfig()->getNode('admin/routers/adminhtml/args/frontName'));
540
+ }
541
+
542
+ public function getRequestModule(Zend_Controller_Request_Http $request) {
543
+ $p = $this->getExplodedPath($request);
544
+ if ($request->getModuleName()) {
545
+ $result = $request->getModuleName();
546
+ }
547
+ else {
548
+ $result = $p[0];
549
+ }
550
+
551
+ return $result;
552
+ }
553
+
554
+ public function getRequestController(Zend_Controller_Request_Http $request) {
555
+ $p = $this->getExplodedPath($request);
556
+ if ($request->getControllerName()) {
557
+ $result = $request->getControllerName();
558
+ }
559
+ else {
560
+ $result = $p[1];
561
+ }
562
+
563
+ return $result;
564
+ }
565
+
566
+ public function getRequestAction(Zend_Controller_Request_Http $request) {
567
+ $p = $this->getExplodedPath($request);
568
+ if ($request->getActionName()) {
569
+ $result = $request->getActionName();
570
+ }
571
+ else {
572
+ $result = $p[2];
573
+ }
574
+
575
+ return $result;
576
+ }
577
+
578
+ public function getExplodedPath(Zend_Controller_Request_Http $request) {
579
+ $defaultPath = array(
580
+ !empty($defaultPath[0]) ? $defaultPath[0] : '',
581
+ !empty($defaultPath[1]) ? $defaultPath[1] : 'index',
582
+ !empty($defaultPath[2]) ? $defaultPath[2] : 'index'
583
+ );
584
+
585
+ $path = trim($request->getPathInfo(), '/');
586
+
587
+ if ($path) {
588
+ $path = explode('/', $path);
589
+ }
590
+ else {
591
+ $path = $defaultPath;
592
+ }
593
+
594
+ return $path;
595
+ }
596
+
597
+ /**
598
+ * @param Mage_Core_Block_Abstract $block
599
+ */
600
+ public function getBlockAlias($block) {
601
+ if (($parent = $block->getParentBlock()) && $this->startsWith($block->getNameInLayout(), $parent->getNameInLayout().'.')) {
602
+ return substr($block->getNameInLayout(), strlen($parent->getNameInLayout() . '.'));
603
+ }
604
+ else {
605
+ return $block->getNameInLayout();
606
+ }
607
+ }
608
+
609
+ public function indexArray($source, $key) {
610
+ $result = array();
611
+ foreach ($source as $value) {
612
+ $result[$value[$key]] = $value;
613
+ }
614
+
615
+ return $result;
616
+ }
617
+
618
+ protected $_attributes = array();
619
+
620
+ public function getAttribute($entityType, $attributeCode, $columns) {
621
+ /* @var $res Mage_Core_Model_Resource */
622
+ $res = Mage::getSingleton('core/resource');
623
+ /* @var $db Varien_Db_Adapter_Pdo_Mysql */
624
+ $db = $res->getConnection('core_read');
625
+
626
+ $key = $entityType . '-' . $attributeCode . '-' . implode('-', $columns);
627
+ if (!isset($this->_attributes[$key])) {
628
+ $this->_attributes[$key] = $db->fetchRow($db->select()
629
+ ->from(array('a' => $res->getTableName('eav_attribute')), $columns)
630
+ ->join(array('t' => $res->getTableName('eav_entity_type')), 't.entity_type_id = a.entity_type_id', null)
631
+ ->where('a.attribute_code = ?', $attributeCode)
632
+ ->where('t.entity_type_code = ?', $entityType));
633
+ }
634
+
635
+ return $this->_attributes[$key];
636
+ }
637
+
638
+ public function getAttributeTable($attribute) {
639
+ return $attribute['backend_table'] ?
640
+ $attribute['backend_table'] :
641
+ 'catalog_category_entity_' . $attribute['backend_type'];
642
+ }
643
+
644
+ /**
645
+ * @param Varien_Db_Adapter_Pdo_Mysql $connection
646
+ * @param $tableName
647
+ * @param array $fields
648
+ * @param bool $onDuplicate
649
+ * @return string
650
+ */
651
+ public function insert($connection, $tableName, $fields = array(), $onDuplicate = true) {
652
+ $sql = "INSERT INTO `{$tableName}` ";
653
+ $sql .= "(`" . implode('`,`', array_keys($fields)) . "`) ";
654
+ $sql .= "VALUES (" . implode(',', $fields) . ") ";
655
+
656
+ if ($onDuplicate && $fields) {
657
+ $sql .= " ON DUPLICATE KEY UPDATE";
658
+ $updateFields = array();
659
+ foreach ($fields as $key => $field) {
660
+ $key = $connection->quoteIdentifier($key);
661
+ $updateFields[] = "{$key}=VALUES({$key})";
662
+ }
663
+ $sql .= " " . implode(', ', $updateFields);
664
+ }
665
+
666
+ return $sql;
667
+ }
668
+
669
+ public function addDotToSuffix($suffix) {
670
+ if ($suffix && $suffix != '/' && strpos($suffix, '.') !== 0) {
671
+ $suffix = '.' . $suffix;
672
+ }
673
+
674
+ return $suffix;
675
+ }
676
+
677
+ /**
678
+ * @param string $helper
679
+ * @return Mana_Core_Helper_PageType[]
680
+ */
681
+ public function getPageTypes($helper = 'helper') {
682
+ if (!isset($this->_pageTypes[$helper])) {
683
+ $result = array();
684
+
685
+ foreach ($this->getSortedXmlChildren(Mage::getConfig()->getNode('mana_core'), 'page_types') as $key => $pageTypeXml) {
686
+ /* @var $pageType Mana_Seo_Helper_PageType */
687
+ $pageType = Mage::helper((string)$pageTypeXml->$helper);
688
+ $pageType->setCode($key);
689
+ $result[$key] = $pageType;
690
+ }
691
+ $this->_pageTypes[$helper] = $result;
692
+ }
693
+
694
+ return $this->_pageTypes[$helper];
695
+ }
696
+
697
+ /**
698
+ * @param string $type
699
+ * @param string $helper
700
+ * @return Mana_Core_Helper_PageType
701
+ */
702
+ public function getPageType($type, $helper = 'helper') {
703
+ $pageTypes = $this->getPageTypes($helper);
704
+
705
+ return $pageTypes[$type];
706
+ }
707
+
708
+ public function isManadevLayeredNavigationInstalled() {
709
+ return $this->isModuleEnabled('Mana_Filters');
710
+ }
711
+
712
+ public function isManadevSeoLayeredNavigationInstalled() {
713
+ return $this->isModuleEnabled('ManaPro_FilterSeoLinks');
714
+ }
715
+
716
+ public function isManadevAttributePageInstalled() {
717
+ return $this->isModuleEnabled('Mana_AttributePage');
718
+ }
719
+
720
+ public function isManadevLayeredNavigationTreeInstalled() {
721
+ return $this->isModuleEnabled('ManaPro_FilterTree');
722
+ }
723
+
724
+ public function isManadevLayeredNavigationColorInstalled()
725
+ {
726
+ return $this->isModuleEnabled('ManaPro_FilterColors');
727
+ }
728
+
729
+ public function isEnterpriseUrlRewriteInstalled() {
730
+ return $this->isModuleEnabled('Enterprise_UrlRewrite');
731
+ }
732
+
733
+ protected $_accentTranslations = array(
734
+ 'à' => 'a',
735
+ 'á' => 'a',
736
+ 'â' => 'a',
737
+ 'ã' => 'a',
738
+ 'ä' => 'a',
739
+ 'ç' => 'c',
740
+ 'è' => 'e',
741
+ 'é' => 'e',
742
+ 'ê' => 'e',
743
+ 'ë' => 'e',
744
+ 'ì' => 'i',
745
+ 'í' => 'i',
746
+ 'î' => 'i',
747
+ 'ï' => 'i',
748
+ 'ñ' => 'n',
749
+ 'ò' => 'o',
750
+ 'ó' => 'o',
751
+ 'ô' => 'o',
752
+ 'õ' => 'o',
753
+ 'ö' => 'o',
754
+ 'ù' => 'u',
755
+ 'ú' => 'u',
756
+ 'û' => 'u',
757
+ 'ü' => 'u',
758
+ 'ý' => 'y',
759
+ 'ÿ' => 'y',
760
+ 'À' => 'A',
761
+ 'Á' => 'A',
762
+ 'Â' => 'A',
763
+ 'Ã' => 'A',
764
+ 'Ä' => 'A',
765
+ 'Ç' => 'C',
766
+ 'È' => 'E',
767
+ 'É' => 'E',
768
+ 'Ê' => 'E',
769
+ 'Ë' => 'E',
770
+ 'Ì' => 'I',
771
+ 'Í' => 'I',
772
+ 'Î' => 'I',
773
+ 'Ï' => 'I',
774
+ 'Ñ' => 'N',
775
+ 'Ò' => 'O',
776
+ 'Ó' => 'O',
777
+ 'Ô' => 'O',
778
+ 'Õ' => 'O',
779
+ 'Ö' => 'O',
780
+ 'Ù' => 'U',
781
+ 'Ú' => 'U',
782
+ 'Û' => 'U',
783
+ 'Ü' => 'U',
784
+ 'Ý' => 'Y',
785
+ );
786
+
787
+ protected $_accentTranslationsOld = array(
788
+ 'from' => 'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ',
789
+ 'to' => 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY'
790
+ );
791
+
792
+ public function unaccent($s) {
793
+ return strtr($s, $this->_accentTranslations);
794
+ }
795
+
796
+ public function getEmptyBlockHtml($block) {
797
+ Mage::dispatchEvent('core_block_abstract_to_html_before', array('block' => $block));
798
+ if (Mage::getStoreConfig('advanced/modules_disable_output/' . $block->getModuleName())) {
799
+ return '';
800
+ }
801
+
802
+ /**
803
+ * Use single transport object instance for all blocks
804
+ */
805
+ $transportObject = new Varien_Object;
806
+ $transportObject->setHtml('');
807
+ Mage::dispatchEvent('core_block_abstract_to_html_after', array('block' => $block, 'transport' => $transportObject));
808
+ $html = $transportObject->getHtml();
809
+
810
+ return $html;
811
+ }
812
  }
app/code/local/Mana/Core/Helper/Debug.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_Debug extends Mage_Core_Helper_Abstract
13
+ {
14
+ public function logLayout($method) {
15
+ $filename = "var/log/layout/" . uniqid() . ".xml";
16
+ Mage::log("$method layout is dumped to {$filename}\n", Zend_Log::DEBUG, 'layout.log');
17
+ $filename = BP.'/'.$filename;
18
+
19
+ $out = '<?xml version="1.0" encoding="UTF-8"?>'."\n<!--\n";
20
+ $out .= "Called from: \n $method\n";
21
+ $out .= "Applied handles: \n";
22
+
23
+ /* @var $layout Mage_Core_Model_Layout */
24
+ $layout = Mage::getSingleton('core/layout');
25
+
26
+ foreach ($layout->getUpdate()->getHandles() as $handle) {
27
+ $out .= " $handle\n";
28
+ }
29
+ $out .= "-->\n";
30
+
31
+ $out .= $layout->getNode()->asNiceXml('');
32
+ if (!is_dir(dirname($filename))) {
33
+ mkdir(dirname($filename), 0777, true);
34
+ }
35
+ file_put_contents($filename, $out);
36
+ }
37
+ }
app/code/local/Mana/Core/Helper/Files.php CHANGED
@@ -12,15 +12,20 @@
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);
@@ -36,10 +41,10 @@ class Mana_Core_Helper_Files extends Mage_Core_Helper_Abstract {
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
  }
@@ -47,7 +52,7 @@ class Mana_Core_Helper_Files extends Mage_Core_Helper_Abstract {
47
  }
48
  else {
49
  if ($this->getFilename($relativeUrl, $type)) {
50
- return $this->getBaseUrl($type).'/'.str_replace(DS, '/', $relativeUrl);
51
  }
52
  else {
53
  return false;
@@ -72,4 +77,29 @@ class Mana_Core_Helper_Files extends Mage_Core_Helper_Abstract {
72
  $i++;
73
  }
74
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
12
  *
13
  */
14
  class Mana_Core_Helper_Files extends Mage_Core_Helper_Abstract {
15
+ protected $skip = array('.', '..');
16
+
17
+ public function getFilename($relativeUrl, $type, $noExistanceCheck = false) {
18
  $result = $this->getBasePath($type).DS.str_replace('/', DS, $relativeUrl);
19
  if (!is_dir(dirname($result))) {
20
  mkdir(dirname($result), 0777, true);
21
  }
22
  return $noExistanceCheck || file_exists($result) ? $result : false;
23
  }
24
+ public function getBaseUrl($type, $baseUrl = null, $storeId = null) {
25
+ if (!$baseUrl) {
26
+ $baseUrl = Mage::app()->getStore($storeId)->getBaseUrl('media');
27
+ }
28
+ return $baseUrl.'m-'.str_replace(DS, '/', $type);
29
  }
30
  public function getBasePath($type) {
31
  return Mage::getConfig()->getOptions()->getMediaDir().DS.'m-'.str_replace('/', DS, $type);
41
  }
42
  return false;
43
  }
44
+ public function getUrl($relativeUrl, $type, $baseUrl = null) {
45
  if (is_array($type)) {
46
  foreach ($type as $candidate) {
47
+ if ($url = $this->getUrl($relativeUrl, $candidate, $baseUrl)) {
48
  return $url;
49
  }
50
  }
52
  }
53
  else {
54
  if ($this->getFilename($relativeUrl, $type)) {
55
+ return $this->getBaseUrl($type, $baseUrl).'/'.str_replace(DS, '/', $relativeUrl);
56
  }
57
  else {
58
  return false;
77
  $i++;
78
  }
79
  }
80
+
81
+ public function walkRecursively($dir, $callback) {
82
+ if (file_exists($dir)) {
83
+ $this->_walkRecursively($dir, $callback);
84
+ }
85
+ }
86
+
87
+ protected function _walkRecursively($dir, $callback) {
88
+ if ($handle = opendir($dir)) {
89
+ $files = array();
90
+ while (false !== ($file = readdir($handle))) {
91
+ $files[] = $file;
92
+ }
93
+ closedir($handle);
94
+ foreach ($files as $file) {
95
+ if (!in_array($file, $this->skip)) {
96
+ $filename = $dir . '/' . $file;
97
+ $isDir = is_dir($filename);
98
+ if (call_user_func($callback, $dir . '/' . $file, $isDir) && $isDir) {
99
+ $this->_walkRecursively($filename, $callback);
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
  }
app/code/local/Mana/Core/Helper/Js.php CHANGED
@@ -11,7 +11,474 @@
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
@@ -45,4 +512,5 @@ jQuery.options($options);
45
  </script>
46
  EOT;
47
  }
 
48
  }
11
  *
12
  */
13
  class Mana_Core_Helper_Js extends Mage_Core_Helper_Abstract {
14
+ #region Dynamic JS and CSS file inclusion
15
+ protected $_fileConfig;
16
+ protected $_moduleIndex = array();
17
+ protected $_tagNames = array('block', 'reference');
18
+ protected $_pathKeys = array('full_path', 'minified_path');
19
+ /**
20
+ * @param Mage_Core_Controller_Varien_Action $action
21
+ * @param Mage_Core_Model_Layout $layout
22
+ */
23
+ public function processFiles($action, $layout) {
24
+ // TODO: refactor phases, find positions, then insert all, then delete all
25
+ Mana_Core_Profiler::start(__METHOD__);
26
+
27
+ $layoutXml = $layout->getNode();
28
+ $layoutDom = new DOMDocument('1.0');
29
+ $layoutNode = $layoutDom->importNode(dom_import_simplexml($layoutXml), true);
30
+ $layoutDom->appendChild($layoutNode);
31
+ $layoutXPath = new DOMXPath($layoutDom);
32
+ $layoutModified = false;
33
+
34
+ $headBlock = $this->_findHeadBlock($layoutXPath);
35
+ $prototypeAction = $this->_findPrototypeAction($layoutXPath);
36
+
37
+ $insertOnDemand = array();
38
+ $insertEverywhere = array();
39
+ $delete = array();
40
+
41
+ $onDemandInsertPlace = null;
42
+ $xpath = '';
43
+ foreach ($this->_getFileConfig() as $name => $fileConfig) {
44
+ /* @var $config array */
45
+ /* @var $options array */
46
+ extract($fileConfig);
47
+
48
+ if ($xpath) {
49
+ $xpath .= ' | ';
50
+ }
51
+ $xpath .= $this->_getActionsXPath($config);
52
+
53
+ }
54
+ foreach ($layoutXPath->query($xpath) as $element) {
55
+ $onDemandInsertPlace = $element;
56
+ break;
57
+ }
58
+
59
+ // handle javascript minification, merging and inclusion on all pages
60
+ foreach ($this->_getFileConfig() as $name => $fileConfig) {
61
+ /* @var $config array */
62
+ /* @var $options array */
63
+ extract($fileConfig);
64
+
65
+ if (in_array('skip', $options)) {
66
+ continue;
67
+ }
68
+
69
+ // find all statically defined actions of including a given script
70
+ $fileActions = $this->_findDomActions($layoutXPath, $config);
71
+
72
+ // prepare layout XML action depending on script parameters
73
+ $action = $this->_createConfigurableAction($layoutDom, $config, $options);
74
+
75
+ // insert script where and if appropriate
76
+ if (in_array('ondemand', $options)) {
77
+ if ($fileAction = $this->_getFirstElement($fileActions)) {
78
+ $insertOnDemand[] = array($action, $onDemandInsertPlace);
79
+ }
80
+ $delete[] = $fileActions;
81
+ }
82
+ elseif (in_array('everywhere', $options)) {
83
+ $insertEverywhere[] = $action;
84
+ foreach (array_reverse($insertOnDemand) as $insertOptions) {
85
+ list($action, $fileAction) = $insertOptions;
86
+ array_unshift($insertEverywhere, $action);
87
+ }
88
+ $insertOnDemand = array();
89
+ $delete[] = $fileActions;
90
+ }
91
+ elseif (in_array('unload', $options)) {
92
+ $delete[] = $fileActions;
93
+ }
94
+ }
95
+
96
+ // insert configurable on demand js files
97
+ foreach ($insertOnDemand as $insertOptions) {
98
+ list($action, $fileAction) = $insertOptions;
99
+ /* @var $fileAction DOMElement */
100
+
101
+ $fileAction->parentNode->insertBefore($action, $fileAction);
102
+ $layoutModified = true;
103
+ }
104
+
105
+ // insert configurable global js files
106
+ if ($prototypeAction) {
107
+ foreach (array_reverse($insertEverywhere) as $action) {
108
+ $prototypeAction->parentNode->insertBefore($action, $prototypeAction->nextSibling);
109
+ $layoutModified = true;
110
+ }
111
+ }
112
+ elseif ($headBlock) {
113
+ foreach (array_reverse($insertEverywhere) as $action) {
114
+ $headBlock->insertBefore($action, $headBlock->firstChild);
115
+ $layoutModified = true;
116
+ }
117
+ }
118
+
119
+ // delete js files includes via layout XML instructions
120
+ foreach ($delete as $fileActions) {
121
+ foreach ($fileActions as $firstFileAction) {
122
+ /* @var $firstFileAction DOMElement */
123
+ $firstFileAction->parentNode->removeChild($firstFileAction);
124
+ $layoutModified = true;
125
+ }
126
+ }
127
+
128
+ if ($layoutModified) {
129
+ $layout->loadDom($layoutNode);
130
+ }
131
+ Mana_Core_Profiler::stop(__METHOD__);
132
+ }
133
+
134
+ public function pageContains($scriptName) {
135
+ $config = $this->_getFileConfig();
136
+ if (isset($config[$scriptName])) {
137
+ $fileConfig = $config[$scriptName];
138
+
139
+ /* @var $config array */
140
+ /* @var $options array */
141
+ extract($fileConfig);
142
+
143
+ if (in_array('ondemand', $options) || in_array('skip', $options)) {
144
+ /* @var $layout Mage_Core_Model_Layout */
145
+ $layout = Mage::getSingleton('core/layout');
146
+
147
+ return count($this->_findSimpleXmlActions($layout->getNode(), $config)) > 0;
148
+ }
149
+ elseif (in_array('everywhere', $options)) {
150
+ return true;
151
+ }
152
+ }
153
+ return false;
154
+ }
155
+
156
+ /**
157
+ * @param DOMXPath $layoutXPath
158
+ * @return DOMElement|null
159
+ */
160
+ protected function _findPrototypeAction($layoutXPath) {
161
+ return $this->_getFirstElement($layoutXPath->query("//block[@name='head']/action" .
162
+ "[@method='addJs' and script='prototype/prototype.js']"));
163
+ }
164
+
165
+ /**
166
+ * @param DOMXPath $layoutXPath
167
+ * @return DOMElement|null
168
+ */
169
+ protected function _findHeadBlock($layoutXPath) {
170
+ return $this->_getFirstElement($layoutXPath->query("//block[@name='head']"));
171
+ }
172
+
173
+ /**
174
+ * @param DOMNodeLIst | DOMNode[] $elements
175
+ * @return DOMElement | null
176
+ */
177
+ protected function _getFirstElement($elements) {
178
+ foreach ($elements as $element) {
179
+ return $element;
180
+ }
181
+ return null;
182
+ }
183
+
184
+ protected function _getActionsXPath($config) {
185
+ $xpath = '';
186
+ foreach ($this->_tagNames as $tagName) {
187
+ foreach ($this->_pathKeys as $pathKey) {
188
+ if (!$config[$pathKey]) {
189
+ continue;
190
+ }
191
+
192
+ if ($xpath) $xpath .= ' | ';
193
+ $xpath .= "//{$tagName}[@name='head']/action";
194
+ if ($config['skinnable']) {
195
+ $xpath .= "[@method='addItem' and type='skin_js' and script='{$config[$pathKey]}']";
196
+ }
197
+ else {
198
+ $xpath .= "[@method='addJs' and script='{$config[$pathKey]}']";
199
+ }
200
+ }
201
+ }
202
+ return $xpath;
203
+ }
204
+
205
+ /**
206
+ * @param DOMXPath $layoutXPath
207
+ * @param array $config
208
+ * @return DOMNode[]
209
+ */
210
+ protected function _findDomActions($layoutXPath, $config) {
211
+ $xpath = $this->_getActionsXPath($config);
212
+ $result = array();
213
+ foreach ($layoutXPath->query($xpath) as $element) {
214
+ $result[] = $element;
215
+ }
216
+ return $result;
217
+ }
218
+
219
+ /**
220
+ * @param SimpleXMLElement $layoutSimpleXml
221
+ * @param array $config
222
+ * @return SimpleXMLElement[]
223
+ */
224
+ protected function _findSimpleXmlActions($layoutSimpleXml, $config) {
225
+ $xpath = $this->_getActionsXPath($config);
226
+ return $layoutSimpleXml->xpath($xpath);
227
+ }
228
+
229
+ /**
230
+ * @param DOMDocument $layoutDom
231
+ * @param array $config
232
+ * @param array $options
233
+ * @return DOMElement
234
+ */
235
+ protected function _createConfigurableAction($layoutDom, $config, $options) {
236
+ $pathKey = 'full_path';
237
+ if (in_array('min', $options) && !empty($config['minified_path'])) {
238
+ $pathKey = 'minified_path';
239
+ }
240
+ $jsXml = $config['skinnable']
241
+ ? '<action method="addItem"><type>skin_js</type><name>' . $config[$pathKey] . '</name><params/></action>'
242
+ : '<action method="addJs"><script>' . $config[$pathKey] . '</script></action>';
243
+ return $layoutDom->importNode(dom_import_simplexml(simplexml_load_string($jsXml)), true);
244
+ }
245
+
246
+ protected function _getFileConfig() {
247
+ if (is_null($this->_fileConfig)) {
248
+ $this->_fileConfig = array();
249
+ $jsConfig = Mage::getConfig()->getNode('mana_core/js');
250
+ if ($jsConfig) {
251
+ $jsChildren = (array)$jsConfig->children();
252
+ uasort($jsChildren, array($this, '_compareByModuleAndSortOrder'));
253
+ foreach ($jsChildren as $name => $config) {
254
+ $options = Mage::getStoreConfig("mana/js/$name");
255
+
256
+ /* @var $config Varien_Simplexml_Element */
257
+ if (empty($config->full_path) || !$options) {
258
+ continue;
259
+ }
260
+
261
+ $options = explode('_', $options);
262
+
263
+ $config = array_merge(array(
264
+ 'type' => 'extension',
265
+ 'skinnable' => false,
266
+ 'full_path' => '',
267
+ 'minified_path' => '',
268
+ 'sort_order' => 1000000,
269
+ ), $config->asArray());
270
+
271
+ $this->_fileConfig[$name] = compact('config', 'options');
272
+ }
273
+ }
274
+ }
275
+ return $this->_fileConfig;
276
+ }
277
+
278
+ public function getSectionSeparator() {
279
+ return "\n91b5970cd70e2353d866806f8003c1cd56646961\n";
280
+ }
281
+
282
+ protected function _compareByModuleAndSortOrder($a, $b) {
283
+ if (($result = $this->_compareByModule($a, $b)) != 0) return $result;
284
+ return $this->_compareBySortOrder($a, $b);
285
+ }
286
+
287
+ protected function _compareByModule($a, $b) {
288
+ $aIndex = empty($a['module']) ? 1000000 : $this->_getModuleIndex((string)$a['module']);
289
+ $bIndex = empty($b['module']) ? 1000000 : $this->_getModuleIndex((string)$b['module']);
290
+ if ($aIndex < $bIndex) return -1;
291
+ if ($aIndex > $bIndex) return 1;
292
+ return 0;
293
+ }
294
+
295
+ protected function _compareBySortOrder($a, $b) {
296
+ $aIndex = empty($a->sort_order) ? 1000000 : ((int)(string)$a->sort_order);
297
+ $bIndex = empty($b->sort_order) ? 1000000 : ((int)(string)$b->sort_order);
298
+ if ($aIndex < $bIndex) return -1;
299
+ if ($aIndex > $bIndex) return 1;
300
+
301
+ return 0;
302
+ }
303
+
304
+ protected function _getModuleIndex($moduleName) {
305
+ if (!isset($this->_moduleIndex[$moduleName])) {
306
+ $result = 0;
307
+ foreach (Mage::getConfig()->getNode('modules')->children() as $name => $module) {
308
+ if ($name = $moduleName) {
309
+ break;
310
+ }
311
+ $result++;
312
+ }
313
+
314
+ $this->_moduleIndex[$moduleName] = $result;
315
+ }
316
+
317
+ return $this->_moduleIndex[$moduleName];
318
+ }
319
+ #endregion
320
+ #region Client side block markup
321
+ public function wrapClientSideBlock($contentHtml, $params = false) {
322
+ $info = $this->parseClientSideBlockInfo($params);
323
+
324
+ if ($info['is_enabled'] && !$info['is_self_contained']) {
325
+ return $info['opening_html'].$contentHtml.$info['closing_html'];
326
+ }
327
+ else {
328
+ return $contentHtml;
329
+ }
330
+
331
+ }
332
+
333
+ /**
334
+ * @param string $blockName
335
+ * @return string
336
+ */
337
+ public function getClientSideBlockName($blockName) {
338
+ return str_replace('.', '-', str_replace('_', '-', $blockName));
339
+ }
340
+
341
+ /**
342
+ * @param bool|array|Mage_Core_Block_Abstract $params
343
+ * @return array
344
+ */
345
+ public function parseClientSideBlockInfo($params = false) {
346
+ /* @var $core Mana_Core_Helper_Data */
347
+ $core = Mage::helper('mana_core');
348
+
349
+ if ($params instanceof Mage_Core_Block_Abstract) {
350
+ $block = $params;
351
+ $params = $block->getMClientSideBlock();
352
+ if (is_array($params) && empty($params['id']) && !$block->getIsAnonymous()) {
353
+ $params['id'] = $this->getClientSideBlockName($block->getNameInLayout());
354
+ }
355
+ }
356
+
357
+ $is_enabled = false;
358
+ $is_self_contained = false;
359
+ $element = 'div';
360
+ $class = '';
361
+ $style = '';
362
+ $attributes = array();
363
+ $opening_html = $closing_html = $class_html = $style_html = $attribute_html = '';
364
+
365
+ if (is_array($params)) {
366
+ $is_enabled = true;
367
+ if (!empty($params['self_contained'])) {
368
+ $is_self_contained = true;
369
+ unset($params['self_contained']);
370
+ }
371
+
372
+ // decide which element to use
373
+ if (!empty($params['element'])) {
374
+ $element = $params['element'];
375
+ unset($params['element']);
376
+ }
377
+
378
+ // assign client side block a unique identifier if possible
379
+ $id = '';
380
+ if (!empty($params['id'])) {
381
+ $id = $params['id'];
382
+ unset($params['id']);
383
+ }
384
+ $class = $id ? 'mb-' . $id : 'm-block';
385
+ $style = '';
386
+
387
+ // set css properties
388
+ if (isset($params['class'])) {
389
+ $class .= " {$params['class']}";
390
+ unset($params['class']);
391
+ }
392
+ foreach ($params as $key => $value) {
393
+ if ($core->startsWith($key, 'style-')) {
394
+ $style .= "{$value};";
395
+ unset($params[$key]);
396
+ }
397
+ }
398
+
399
+ // decide on client side block type
400
+ if (empty($params['m-block'])) {
401
+ $type = '';
402
+ if (!empty($params['type'])) {
403
+ $type = $params['type'];
404
+ if (in_array($type, array('Mana/Core/Block', 'Mana/Core/NamedBlock'))) {
405
+ $type = '';
406
+ }
407
+ unset($params['type']);
408
+ }
409
+ if ($type) {
410
+ $params['m-block'] = $type;
411
+ }
412
+ }
413
+
414
+ // render client side block element and its contents
415
+ $opening_html = '<' . $element;
416
+ if (isset($params['html'])) {
417
+ foreach ($params['html'] as $key => $value) {
418
+ $attributes[$key] = $value;
419
+ if ($attribute_html) $attribute_html .= ' ';
420
+ $attribute_html .= "$key=\"$value\"";
421
+ }
422
+ unset($params['html']);
423
+ }
424
+ foreach ($params as $key => $value) {
425
+ $attributeKey = 'data-' . str_replace('.', '-', str_replace('_', '-', $key));
426
+ $attributeValue = str_replace('"', '&quot;', str_replace('<', '&lt;', str_replace('>', '&gt;', $value)));
427
+ $attributes[$attributeKey] = $attributeValue;
428
+
429
+ if ($attribute_html) $attribute_html .= ' ';
430
+ $attribute_html .= "$attributeKey=\"$attributeValue\"";
431
+ }
432
+ if ($attribute_html) {
433
+ $opening_html .= ' ' . $attribute_html;
434
+ }
435
+
436
+ if ($class) {
437
+ $class_html = 'class="' . $class . '"';
438
+ $opening_html .= ' '.$class_html;
439
+ }
440
+ if ($style) {
441
+ $style_html = 'style="' . $style . '"';
442
+ $opening_html .= ' ' . $style_html;
443
+ }
444
+ $opening_html .= '>';
445
+ $closing_html = '</' . $element . '>';
446
+
447
+ }
448
+
449
+ return compact('is_enabled', 'is_self_contained', 'element', 'class', 'style', 'attributes',
450
+ 'opening_html', 'closing_html', 'class_html', 'style_html', 'attribute_html');
451
+ }
452
+
453
+ public function setConfig($key, $value) {
454
+ /* @var $layout Mage_Core_Model_Layout */
455
+ $layout = Mage::getSingleton(strtolower('Core/Layout'));
456
+ /* @var $jsBlock Mana_Core_Block_Js */
457
+ $jsBlock = $layout->getBlock('m_js');
458
+
459
+ if ($jsBlock) {
460
+ $jsBlock->setConfig($key, $value);
461
+ }
462
+
463
+ return $this;
464
+ }
465
+
466
+ public function getConfig() {
467
+ /* @var $layout Mage_Core_Model_Layout */
468
+ $layout = Mage::getSingleton(strtolower('Core/Layout'));
469
+ /* @var $jsBlock Mana_Core_Block_Js */
470
+ $jsBlock = $layout->getBlock('m_js');
471
+
472
+ if ($jsBlock) {
473
+ return $jsBlock->getConfig();
474
+ }
475
+
476
+ return false;
477
+ }
478
+
479
+ #endregion
480
+ #region Deprecated API for $.options() and $.__() functions
481
+ /**
482
  * Makes translations of specified strings to be available in client-side scripts.
483
  * @param array $translations
484
  * @return Mana_Core_Helper_Js
512
  </script>
513
  EOT;
514
  }
515
+ #endregion
516
  }
app/code/local/Mana/Core/Helper/Json.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://www.manadev.com/license Proprietary License
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Core_Helper_Json extends Mage_Core_Helper_Abstract {
13
+ public function encodeAttribute($data) {
14
+ /* @var $core Mana_Core_Helper_Data */
15
+ $core = Mage::helper('mana_core');
16
+
17
+ $result = $core->jsonForceObjectAndEncode($data);
18
+ $result = implode("\"", str_replace("\"", "'", explode("'", $result)));
19
+ $result = $this->escapeHtml($result);
20
+ return $result;
21
+ }
22
+ }
app/code/local/Mana/Core/Helper/Layer.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_Layer extends Mage_Core_Helper_Abstract {
13
+ public function useSolrForNavigation() {
14
+ if (!Mage::helper('core')->isModuleEnabled('Enterprise_Search')) {
15
+ return false;
16
+ }
17
+ /* @var $helper Enterprise_Search_Helper_Data */
18
+ $helper = Mage::helper('enterprise_search');
19
+
20
+ return $helper->getIsEngineAvailableForNavigation();
21
+ }
22
+
23
+ public function useSolrForSearch() {
24
+ if (!Mage::helper('core')->isModuleEnabled('Enterprise_Search')) {
25
+ return false;
26
+ }
27
+ /* @var $helper Enterprise_Search_Helper_Data */
28
+ $helper = Mage::helper('enterprise_search');
29
+
30
+ return $helper->isThirdPartSearchEngine() && $helper->isActiveEngine();
31
+ }
32
+
33
+ public function useSolr() {
34
+ switch ($this->getMode()) {
35
+ case 'category':
36
+ return $this->useSolrForNavigation();
37
+ case 'search':
38
+ return $this->useSolrForSearch();
39
+ default:
40
+ throw new Exception('Not implemented');
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @param null $mode
46
+ * @throws Exception
47
+ * @return Mage_Catalog_Model_Layer
48
+ */
49
+ public function getLayer($mode = null) {
50
+ if (!$mode) {
51
+ $mode = $this->getMode();
52
+ }
53
+ switch ($mode) {
54
+ case 'category':
55
+ return Mage::getSingleton($this->useSolrForNavigation()
56
+ ? 'enterprise_search/catalog_layer'
57
+ : 'catalog/layer'
58
+ );
59
+ case 'search':
60
+ return Mage::getSingleton($this->useSolrForSearch()
61
+ ? 'enterprise_search/search_layer'
62
+ : 'catalogsearch/layer'
63
+ );
64
+ default:
65
+ throw new Exception('Not implemented');
66
+ }
67
+ }
68
+
69
+ protected $_mode;
70
+
71
+ public function getMode() {
72
+ if ($this->_mode) {
73
+ return $this->_mode;
74
+ }
75
+ elseif (in_array(Mage::helper('mana_core')->getRoutePath(), array('catalogsearch/result/index', 'manapro_filterajax/search/index'))) {
76
+ return 'search';
77
+ }
78
+ else {
79
+ return 'category';
80
+ }
81
+ }
82
+
83
+ public function setMode($mode) {
84
+ $this->_mode = $mode;
85
+
86
+ return $this;
87
+ }
88
+
89
+ }
app/code/local/Mana/Core/Helper/Layout.php CHANGED
@@ -34,4 +34,18 @@ class Mana_Core_Helper_Layout extends Mage_Core_Helper_Abstract {
34
  if ($a['sortOrder'] > $b['sortOrder']) return 1;
35
  return 0;
36
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
34
  if ($a['sortOrder'] > $b['sortOrder']) return 1;
35
  return 0;
36
  }
37
+
38
+ public function renderBlock($blockName) {
39
+ /* @var $layout Mage_Core_Model_Layout */
40
+ $layout = Mage::getSingleton('core/layout');
41
+
42
+ /* @var $url Mage_Core_Model_Url */
43
+ $url = Mage::getSingleton('core/url');
44
+ if ($block = $layout->getBlock($blockName)) {
45
+ return $url->sessionUrlVar($block->toHtml());
46
+ }
47
+ else {
48
+ return '';
49
+ }
50
+ }
51
  }
app/code/local/Mana/Core/Helper/Logger.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://www.manadev.com/license Proprietary License
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ * @method Mana_Core_Helper_Logger beginSeoMatch(string $message = null)
11
+ * @method Mana_Core_Helper_Logger logSeoMatch(string $message = null)
12
+ * @method Mana_Core_Helper_Logger endSeoMatch(string $message = null)
13
+
14
+ * @method Mana_Core_Helper_Logger beginSeoUrl(string $message = null)
15
+ * @method Mana_Core_Helper_Logger logSeoUrl(string $message = null)
16
+ * @method Mana_Core_Helper_Logger endSeoUrl(string $message = null)
17
+
18
+ * @method Mana_Core_Helper_Logger beginDbIndexerFailure(string $message = null)
19
+ * @method Mana_Core_Helper_Logger logDbIndexerFailure(string $message = null)
20
+ * @method Mana_Core_Helper_Logger endDbIndexerFailure(string $message = null)
21
+
22
+ * @method Mana_Core_Helper_Logger beginUrlIndexer(string $message = null)
23
+ * @method Mana_Core_Helper_Logger logUrlIndexer(string $message = null)
24
+ * @method Mana_Core_Helper_Logger endUrlIndexer(string $message = null)
25
+
26
+ * @method Mana_Core_Helper_Logger beginTemp(string $message = null)
27
+ * @method Mana_Core_Helper_Logger logTemp(string $message = null)
28
+ * @method Mana_Core_Helper_Logger endTemp(string $message = null)
29
+ */
30
+ class Mana_Core_Helper_Logger extends Mage_Core_Helper_Abstract {
31
+ const INDENT_WIDTH = 4;
32
+ protected $_isEnabled = array(
33
+ 'all' => false,
34
+ 'seo_match' => false,
35
+ 'seo_url' => false,
36
+ 'db_indexer_failure' => false,
37
+ 'url_indexer' => false,
38
+ 'temp' => true,
39
+ );
40
+ protected $_indent = array('all' => 0);
41
+
42
+ public function __call($method, $args) {
43
+ /* @var $core Mana_Core_Helper_Data */
44
+ $core = Mage::helper('mana_core');
45
+ if ($core->startsWith($method, 'log')) {
46
+ $key = $this->_underscore(substr($method, 3));
47
+ if ($this->_isEnabled[$key]) {
48
+ if (!isset($this->_indent[$key])) {
49
+ $this->_indent[$key] = 0;
50
+ }
51
+ if (!empty($args[0])) {
52
+ Mage::log(str_repeat(' ', $this->_indent[$key]) . $args[0], Zend_Log::DEBUG, "m_$key.log");
53
+ }
54
+ if ($key != 'all') {
55
+ if (!empty($args[0])) {
56
+ Mage::log(str_repeat(' ', $this->_indent['all']) . $args[0], Zend_Log::DEBUG, "m_all.log");
57
+ }
58
+ }
59
+ }
60
+
61
+ return $this;
62
+ }
63
+ elseif ($core->startsWith($method, 'begin')) {
64
+ $key = $this->_underscore(substr($method, 5));
65
+ if ($this->_isEnabled[$key]) {
66
+ if (!isset($this->_indent[$key])) {
67
+ $this->_indent[$key] = 0;
68
+ }
69
+ if (!empty($args[0])) {
70
+ Mage::log(str_repeat(' ', $this->_indent[$key]). $args[0], Zend_Log::DEBUG, "m_$key.log");
71
+ }
72
+ $this->_indent[$key] += self::INDENT_WIDTH;
73
+ if ($key != 'all') {
74
+ if (!empty($args[0])) {
75
+ Mage::log(str_repeat(' ', $this->_indent['all']) . $args[0], Zend_Log::DEBUG, "m_all.log");
76
+ }
77
+ $this->_indent['all'] += self::INDENT_WIDTH;
78
+ }
79
+ }
80
+
81
+ return $this;
82
+ }
83
+ elseif ($core->startsWith($method, 'end')) {
84
+ $key = $this->_underscore(substr($method, 3));
85
+ if ($this->_isEnabled[$key]) {
86
+ if (!isset($this->_indent[$key])) {
87
+ $this->_indent[$key] = 0;
88
+ }
89
+ $this->_indent[$key] -= self::INDENT_WIDTH;
90
+ if (!empty($args[0])) {
91
+ Mage::log(str_repeat(' ', $this->_indent[$key]) . $args[0], Zend_Log::DEBUG, "m_$key.log");
92
+ }
93
+ if ($key != 'all') {
94
+ $this->_indent['all'] -= self::INDENT_WIDTH;
95
+ if (!empty($args[0])) {
96
+ Mage::log(str_repeat(' ', $this->_indent['all']) . $args[0], Zend_Log::DEBUG, "m_all.log");
97
+ }
98
+ }
99
+ }
100
+
101
+ return $this;
102
+ }
103
+
104
+ throw new Exception("Invalid method " . get_class($this) . "::" . $method . "(" . print_r($args, 1) . ")");
105
+ }
106
+
107
+ protected function _underscore($name) {
108
+ return strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $name));
109
+ }
110
+ }
app/code/local/Mana/Core/Helper/Mbstring.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_Mbstring extends Mage_Core_Helper_Abstract {
13
+ protected $_multiByte;
14
+ public function __construct() {
15
+ $this->_multiByte = function_exists('mb_strpos');
16
+ }
17
+ public function strpos($haystack, $needle, $offset = 0) {
18
+ if ($this->_multiByte) {
19
+ return mb_strpos($haystack, $needle, $offset);
20
+ }
21
+ else {
22
+ return strpos($haystack, $needle, $offset);
23
+ }
24
+ }
25
+
26
+ public function strrpos($haystack, $needle, $offset = null) {
27
+ if ($this->_multiByte) {
28
+ return is_null($offset) ? mb_strrpos($haystack, $needle) : mb_strrpos($haystack, $needle, $offset);
29
+ }
30
+ else {
31
+ return is_null($offset) ? strrpos($haystack, $needle) : strrpos($haystack, $needle, $offset);
32
+ }
33
+ }
34
+
35
+ public function strlen($haystack) {
36
+ if ($this->_multiByte) {
37
+ return mb_strlen($haystack);
38
+ }
39
+ else {
40
+ return strlen($haystack);
41
+ }
42
+ }
43
+
44
+ public function substr($string, $start, $length = null) {
45
+ if ($this->_multiByte) {
46
+ return is_null($length) ? mb_substr($string, $start) : mb_substr($string, $start, $length);
47
+ }
48
+ else {
49
+ return is_null($length) ? substr($string, $start) : substr($string, $start, $length);
50
+ }
51
+ }
52
+
53
+ public function endsWith($haystack, $needle) {
54
+ return ($this->strrpos($haystack, $needle) === $this->strlen($haystack) - $this->strlen($needle));
55
+ }
56
+
57
+ public function startsWith($haystack, $needle) {
58
+ return ($this->strpos($haystack, $needle) === 0);
59
+ }
60
+
61
+ public function stripos($haystack, $needle, $offset = 0) {
62
+ if ($this->_multiByte) {
63
+ return mb_stripos($haystack, $needle, $offset);
64
+ }
65
+ else {
66
+ return stripos($haystack, $needle, $offset);
67
+ }
68
+ }
69
+ }
app/code/local/Mana/Core/Helper/PageType.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://www.manadev.com/license Proprietary License
7
+ */
8
+ abstract class Mana_Core_Helper_PageType extends Mage_Core_Helper_Abstract {
9
+ protected $_code;
10
+
11
+ abstract public function getRoutePath();
12
+ abstract public function getCurrentSuffix();
13
+ public function setCode($code) {
14
+ $this->_code = $code;
15
+ return $this;
16
+ }
17
+ public function getCode() {
18
+ return $this->_code;
19
+ }
20
+
21
+ public function matchRoute($route) {
22
+ return $route == $this->getRoutePath();
23
+ }
24
+ }
app/code/local/Mana/Core/Helper/PageType/Category.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://www.manadev.com/license Proprietary License
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Core_Helper_PageType_Category extends Mana_Core_Helper_PageType {
13
+ public function getCurrentSuffix() {
14
+ /* @var $categoryHelper Mage_Catalog_Helper_Category */
15
+ $categoryHelper = Mage::helper('catalog/category');
16
+
17
+ return $categoryHelper->getCategoryUrlSuffix();
18
+ }
19
+
20
+
21
+ public function getRoutePath() {
22
+ return 'catalog/category/view';
23
+ }
24
+ }
app/code/local/Mana/Core/Helper/PageType/CmsPage.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_PageType_CmsPage extends Mana_Core_Helper_PageType {
13
+ public function getCurrentSuffix() {
14
+ return Mage::getStoreConfig('mana/seo/cms_page_suffix');
15
+ }
16
+
17
+ public function getRoutePath() {
18
+ return 'cms/page/view';
19
+ }
20
+ }
app/code/local/Mana/Core/Helper/PageType/HomePage.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_PageType_HomePage extends Mana_Core_Helper_PageType {
13
+ public function getCurrentSuffix() {
14
+ return Mage::getStoreConfig('mana/seo/home_page_suffix');
15
+ }
16
+
17
+ public function getRoutePath() {
18
+ return 'cms/index/index';
19
+ }
20
+ }
app/code/local/Mana/Core/Helper/PageType/Search.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_PageType_Search extends Mana_Core_Helper_PageType {
13
+ public function getCurrentSuffix() {
14
+ return Mage::getStoreConfig('mana/seo/search_suffix');
15
+ }
16
+
17
+ public function getRoutePath() {
18
+ return 'catalogsearch/result/index';
19
+ }
20
+ }
app/code/local/Mana/Core/Helper/Router.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_Router extends Mage_Core_Helper_Abstract {
13
+ public function forward($route, $request = null, $params = null, $query = null) {
14
+ if (is_array($route)) {
15
+ $this->forward(
16
+ $route['route'],
17
+ isset($action['request']) ? $action['request'] : null,
18
+ isset($action['params']) ? $action['params'] : null,
19
+ isset($action['query']) ? $action['query'] : null
20
+ );
21
+
22
+ return $this;
23
+ }
24
+ if (!$request) {
25
+ $request = Mage::app()->getRequest();
26
+ }
27
+
28
+ $request->initForward();
29
+
30
+ if (!is_null($params)) {
31
+ $request->setParams($params);
32
+ }
33
+
34
+ list($module, $controller, $action) = explode('/', $route);
35
+
36
+ if ($controller != '*') {
37
+ $request->setControllerName($controller);
38
+
39
+ }
40
+
41
+ if ($module != '*') {
42
+ $request->setModuleName($module);
43
+ }
44
+
45
+ if ($action != '*') {
46
+ $request->setActionName($action);
47
+ }
48
+
49
+ $request->setDispatched(false);
50
+
51
+ if (!is_null($query)) {
52
+ $_GET = $query;
53
+ }
54
+
55
+ return $this;
56
+ }
57
+
58
+ public function changePath($path, $request = null) {
59
+ if (!$request) {
60
+ $request = Mage::app()->getRequest();
61
+ }
62
+
63
+ if ($path === '') {
64
+ $path = '/';
65
+ }
66
+ $request
67
+ ->setPathInfo($path)
68
+ ->setModuleName(null)
69
+ ->setControllerName(null)
70
+ ->setActionName(null)
71
+ ->setDispatched(false);
72
+
73
+ return $this;
74
+ }
75
+
76
+ public function processWithoutRendering($target, $method) {
77
+ /* @var $core Mana_Core_Helper_Data */
78
+ $core = Mage::helper('mana_core');
79
+
80
+ Mage::register('m_response_callback', array($target, $method));
81
+ if ($core->inAdmin() && $target instanceof Mage_Adminhtml_Controller_Action) {
82
+ /* @var $adminSession Mage_Adminhtml_Model_Session */
83
+ $adminSession = Mage::getSingleton('adminhtml/session');
84
+ $adminSession->setDataUsingMethod('is_url_notice', $target->getFlag('', 'check_url_settings'));
85
+ }
86
+ Mage::app()->getFrontController()->setDataUsingMethod('no_render', true);
87
+
88
+ return $this;
89
+ }
90
+
91
+ }
app/code/local/Mana/Core/Helper/Seo.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_Seo extends Mage_Core_Helper_Abstract {
13
+ protected $_urlSymbols;
14
+
15
+ protected function _initUrlSymbols() {
16
+ if (!$this->_urlSymbols) {
17
+ $this->_urlSymbols = array();
18
+ $this->_urlSymbols['-'] = Mage::getStoreConfig('mana/seo_symbols/dash');
19
+ $this->_urlSymbols['/'] = Mage::getStoreConfig('mana/seo_symbols/slash');
20
+ $this->_urlSymbols['+'] = Mage::getStoreConfig('mana/seo_symbols/plus');
21
+ $this->_urlSymbols['_'] = Mage::getStoreConfig('mana/seo_symbols/underscore');
22
+ $this->_urlSymbols["'"] = Mage::getStoreConfig('mana/seo_symbols/quote');
23
+ $this->_urlSymbols['"'] = Mage::getStoreConfig('mana/seo_symbols/double_quote');
24
+ $this->_urlSymbols['%'] = Mage::getStoreConfig('mana/seo_symbols/percent');
25
+ $this->_urlSymbols['#'] = Mage::getStoreConfig('mana/seo_symbols/hash');
26
+ $this->_urlSymbols['&'] = Mage::getStoreConfig('mana/seo_symbols/ampersand');
27
+ $this->_urlSymbols[' '] = Mage::getStoreConfig('mana/seo_symbols/space');
28
+ }
29
+
30
+ return $this;
31
+ }
32
+
33
+ public function encode($text) {
34
+ $this->_initUrlSymbols();
35
+ foreach ($this->_urlSymbols as $symbol => $urlSymbol) {
36
+ $text = str_replace($symbol, $urlSymbol, $text);
37
+ }
38
+
39
+ return $text;
40
+ }
41
+
42
+ public function decode($text) {
43
+ $this->_initUrlSymbols();
44
+ $result = '';
45
+ for ($i = 0; $i < mb_strlen($text);) {
46
+ $found = false;
47
+ foreach ($this->_urlSymbols as $symbol => $urlSymbol) {
48
+ if (mb_strpos($text, $urlSymbol, $i) === $i) {
49
+ $result .= $symbol;
50
+ $i += mb_strlen($urlSymbol);
51
+ $found = true;
52
+ break;
53
+ }
54
+ }
55
+ if (!$found) {
56
+ $result .= mb_substr($text, $i++, 1);
57
+ }
58
+ }
59
+
60
+ return $result;
61
+ }
62
+
63
+ public function select($expr) {
64
+ $this->_initUrlSymbols();
65
+ foreach ($this->_urlSymbols as $symbol => $urlSymbol) {
66
+ $escapedSymbol = str_replace("'", "\\'", $symbol);
67
+ $expr = "REPLACE($expr, '$escapedSymbol', '$urlSymbol')";
68
+ }
69
+
70
+ return $expr;
71
+ }
72
+ }
app/code/local/Mana/Core/Helper/UrlTemplate.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://www.manadev.com/license Proprietary License
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Core_Helper_UrlTemplate extends Mage_Core_Helper_Abstract {
13
+ public function encodeAttribute($data) {
14
+ if (Mage::app()->getStore()->isAdmin() || Mage::getStoreConfigFlag('mana/ajax/debug')) {
15
+ return $data;
16
+ }
17
+ else {
18
+ return $this->urlEncode($data);
19
+ }
20
+ }
21
+ }
app/code/local/Mana/Core/Helper/Utils.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Helper_Utils extends Mage_Core_Helper_Abstract {
13
+ /**
14
+ * @return Mana_Core_Helper_Utils
15
+ */
16
+ public function reindexAll() {
17
+ /* @var $indexer Mage_Index_Model_Indexer */
18
+ $indexer = Mage::getSingleton('index/indexer');
19
+
20
+ Mage::dispatchEvent('shell_reindex_init_process');
21
+ foreach ($indexer->getProcessesCollection() as $process) {
22
+ /* @var $process Mage_Index_Model_Process */
23
+ $process->reindexEverything();
24
+ Mage::dispatchEvent($process->getIndexerCode() . '_shell_reindex_after');
25
+ }
26
+ Mage::dispatchEvent('shell_reindex_finalize_process');
27
+
28
+ return $this;
29
+ }
30
+
31
+ public function reindex($code) {
32
+ /* @var $indexer Mage_Index_Model_Indexer */
33
+ $indexer = Mage::getSingleton('index/indexer');
34
+
35
+ $indexer->getProcessByCode($code)->reindexAll();
36
+
37
+ return $this;
38
+ }
39
+
40
+ /**
41
+ * @param string $module
42
+ * @return Mana_Core_Helper_Utils
43
+ */
44
+ public function disableModuleOutput($module) {
45
+ /* @var $configData Mage_Core_Model_Config_Data */
46
+ $configData = Mage::getModel('core/config_data');
47
+ /* @noinspection PhpUndefinedMethodInspection */
48
+ $configData
49
+ ->setScope('default')
50
+ ->setScopeId(0)
51
+ ->setPath('advanced/modules_disable_output/'. $module)
52
+ ->setValue(1)
53
+ ->save();
54
+
55
+ return $this;
56
+ }
57
+
58
+ /**
59
+ * @return Mana_Core_Helper_Utils
60
+ */
61
+ public function clearDiskCache() {
62
+ /* @var $fileHelper Mana_Core_Helper_Files */
63
+ $fileHelper = Mage::helper('mana_core/files');
64
+ $fileHelper->walkRecursively(Mage::getBaseDir('cache'), array($this, '_clearDiskCacheEntry'));
65
+
66
+ return $this;
67
+ }
68
+
69
+ public function _clearDiskCacheEntry($filename, $isDir) {
70
+ if (!$isDir) {
71
+ unlink($filename);
72
+ }
73
+
74
+ return true;
75
+ }
76
+
77
+ public function setStoreConfig($path, $value, $scope = 'default', $scopeId = 0) {
78
+ /* @var $res Mage_Core_Model_Resource */
79
+ $res = Mage::getSingleton('core/resource');
80
+
81
+ /* @var $db Varien_Db_Adapter_Pdo_Mysql */
82
+ $db = $res->getConnection('write');
83
+
84
+ /* @var $core Mana_Core_Helper_Data */
85
+ $core = Mage::helper('mana_core');
86
+
87
+ $db->query($core->insert($db, $res->getTableName('core/config_data'), array(
88
+ 'scope' => "'$scope'",
89
+ 'scope_id' => $scopeId,
90
+ 'path' => "'$path'",
91
+ 'value' => "'$value'",
92
+ )));
93
+
94
+ return $this;
95
+ }
96
+
97
+ public function getStoreConfig($path) {
98
+ $scope = 'default';
99
+ $scopeId = 0;
100
+
101
+ /* @var $collection Mage_Core_Model_Mysql4_Config_Data_Collection */
102
+ $collection = Mage::getModel('core/config_data')->getCollection();
103
+
104
+ $collection->getSelect()
105
+ ->where('scope=?', $scope)
106
+ ->where('scope_id=?', $scopeId)
107
+ ->where('path=?', $path);
108
+
109
+ /** @noinspection PhpUnusedLocalVariableInspection */
110
+ foreach ($collection as $result) {
111
+ /* @noinspection PhpUndefinedMethodInspection */
112
+ return $result->getValue();
113
+ }
114
+
115
+ return (string)Mage::getConfig()->getNode('default/'.$path);
116
+ }
117
+
118
+ }
app/code/local/Mana/Core/Model/Callback.php CHANGED
@@ -14,15 +14,15 @@
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 {
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 {
app/code/local/Mana/Core/Model/Closure.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Model_Closure extends Varien_Object {
13
+ /**
14
+ * @return mixed
15
+ */
16
+ public function execute() {
17
+ }
18
+ }
app/code/local/Mana/Core/Model/Indexer.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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
+ abstract class Mana_Core_Model_Indexer extends Mage_Index_Model_Indexer_Abstract {
13
+ protected $_code;
14
+ protected $_process;
15
+ protected $_xml = null;
16
+ public function getCode() {
17
+ return $this->_code;
18
+ }
19
+
20
+ /**
21
+ * @return Mage_Index_Model_Process | bool
22
+ */
23
+ public function getProcess() {
24
+ if (!$this->_process) {
25
+ $this->_process = Mage::getModel('index/process')->load($this->getCode(), 'indexer_code');
26
+ }
27
+
28
+ return $this->_process;
29
+ }
30
+
31
+ /**
32
+ * @return Varien_Simplexml_Element | bool
33
+ */
34
+ public function getXml() {
35
+ if (is_null($this->_xml)) {
36
+ $result = Mage::getConfig()->getXpath("//global/index/indexer/{$this->getProcess()->getIndexerCode()}");
37
+
38
+ $this->_xml = count($result) == 1 ? $result[0] : false;
39
+ }
40
+ return $this->_xml;
41
+ }
42
+
43
+ /**
44
+ * Get Indexer name
45
+ *
46
+ * @return string
47
+ */
48
+ public function getName() {
49
+ /** @noinspection PhpUndefinedFieldInspection */
50
+ return (string)$this->getXml()->name;
51
+ }
52
+
53
+ /**
54
+ * Retrieve Indexer description
55
+ *
56
+ * @return string
57
+ */
58
+ public function getDescription() {
59
+ /** @noinspection PhpUndefinedFieldInspection */
60
+ return (string)$this->getXml()->description;
61
+ }
62
+ }
app/code/local/Mana/Core/Model/Observer.php CHANGED
@@ -58,13 +58,14 @@ class Mana_Core_Model_Observer {
58
  /* @var $layout Mage_Core_Model_Layout */
59
  $layout = $observer->getEvent()->getLayout();
60
 
 
61
  if ($node = Mage::getConfig()->getNode('m_block_layout_handle')) {
62
  foreach ($node->children() as $ruleName => $ruleConfig) {
63
  $if = $ruleConfig->if;
64
  $handleName = (string)$ruleConfig->load_handle;
65
  if ($type = (string)$if['type']) {
66
  if ($this->_hasBlockInXml($type, $layout)) {
67
- $action->getLayout()->getUpdate()->fetchPackageLayoutUpdates((string)$handleName);
68
  }
69
  }
70
  }
@@ -91,8 +92,10 @@ class Mana_Core_Model_Observer {
91
  /* @var $action Mage_Core_Controller_Varien_Action */ $action = $observer->getEvent()->getAction();
92
  /* @var $layout Mage_Core_Model_Layout */ $layout = $observer->getEvent()->getLayout();
93
 
 
94
  Mage::helper('mana_core/layout')->prepareDelayedLayoutBlocks();
95
 
 
96
  if (Mage::getConfig()->getNode('m_blocks')) {
97
  foreach (Mage::getConfig()->getNode('m_blocks')->children() as $name => $config) {
98
  if (in_array($name, $layout->getUpdate()->getHandles())) {
@@ -118,6 +121,7 @@ class Mana_Core_Model_Observer {
118
  }
119
  }
120
  }
 
121
  }
122
 
123
 
@@ -225,5 +229,122 @@ class Mana_Core_Model_Observer {
225
  Mage::register('m_page_is_being_rendered', true);
226
  }
227
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  }
229
 
58
  /* @var $layout Mage_Core_Model_Layout */
59
  $layout = $observer->getEvent()->getLayout();
60
 
61
+ // load javascript and css files if certain blocks are present
62
  if ($node = Mage::getConfig()->getNode('m_block_layout_handle')) {
63
  foreach ($node->children() as $ruleName => $ruleConfig) {
64
  $if = $ruleConfig->if;
65
  $handleName = (string)$ruleConfig->load_handle;
66
  if ($type = (string)$if['type']) {
67
  if ($this->_hasBlockInXml($type, $layout)) {
68
+ $layout->getUpdate()->fetchPackageLayoutUpdates((string)$handleName);
69
  }
70
  }
71
  }
92
  /* @var $action Mage_Core_Controller_Varien_Action */ $action = $observer->getEvent()->getAction();
93
  /* @var $layout Mage_Core_Model_Layout */ $layout = $observer->getEvent()->getLayout();
94
 
95
+ // additional layout changes initiated by blocks when all their properties and children are already loaded
96
  Mage::helper('mana_core/layout')->prepareDelayedLayoutBlocks();
97
 
98
+ #region Obsolete, currently does nothing as there is no more m_blocks section in config.xml
99
  if (Mage::getConfig()->getNode('m_blocks')) {
100
  foreach (Mage::getConfig()->getNode('m_blocks')->children() as $name => $config) {
101
  if (in_array($name, $layout->getUpdate()->getHandles())) {
121
  }
122
  }
123
  }
124
+ #endregion
125
  }
126
 
127
 
229
  Mage::register('m_page_is_being_rendered', true);
230
  }
231
  }
232
+
233
+ /**
234
+ * REPLACE THIS WITH DESCRIPTION (handles event "controller_action_layout_generate_blocks_before")
235
+ * @param Varien_Event_Observer $observer
236
+ */
237
+ public function processJsCssFiles($observer) {
238
+ /* @var $action Mage_Core_Controller_Varien_Action */
239
+ $action = $observer->getEvent()->getAction();
240
+ /* @var $layout Mage_Core_Model_Layout */
241
+ $layout = $observer->getEvent()->getLayout();
242
+
243
+ /* @var $helper Mana_Core_Helper_Js */
244
+ $helper = Mage::helper('mana_core/js');
245
+
246
+ $helper->processFiles($action, $layout);
247
+ }
248
+
249
+ /**
250
+ * wrap all blocks marked as client side blocks into container elements
251
+ * (handles event "core_block_abstract_to_html_after")
252
+ * @param Varien_Event_Observer $observer
253
+ */
254
+ public function addClientSideBlockMarkup($observer) {
255
+ /* @var $block Mage_Core_Block_Abstract */
256
+ $block = $observer->getEvent()->getBlock();
257
+ /* @var $transport Varien_Object */
258
+ $transport = $observer->getEvent()->getTransport();
259
+
260
+ if ($block->getNameInLayout() == 'head' && ($css = $block->getMCss())) {
261
+ /* @var $files Mana_Core_Helper_Files */
262
+ $files = Mage::helper(strtolower('Mana_Core/Files'));
263
+ $html = '';
264
+ foreach ($css as $relativeUrl) {
265
+ if ($files->getFilename($relativeUrl, 'css')) {
266
+ $html .= '<link rel="stylesheet" type="text/css" href="' . $files->getUrl($relativeUrl, 'css') . '" />' . "\n";
267
+ }
268
+ }
269
+ if ($html) {
270
+ $transport->setHtml($transport->getHtml() . $html);
271
+ }
272
+ }
273
+
274
+ /* @var $js Mana_Core_Helper_Js */
275
+ $js = Mage::helper('mana_core/js');
276
+ $transport->setHtml($js->wrapClientSideBlock($transport->getHtml(), $block));
277
+ }
278
+
279
+ /**
280
+ * REPLACE THIS WITH DESCRIPTION (handles event "core_block_abstract_to_html_after")
281
+ * @param Varien_Event_Observer $observer
282
+ */
283
+ public function renderRequireSettings($observer) {
284
+ /* @var $block Mage_Core_Block_Abstract */
285
+ $block = $observer->getEvent()->getBlock();
286
+ /* @var $transport Varien_Object */
287
+ $transport = $observer->getEvent()->getTransport();
288
+
289
+ /* @var $js Mana_Core_Helper_Js */
290
+ $js = Mage::helper('mana_core/js');
291
+
292
+ if ($block->getNameInLayout() == 'head' && $js->pageContains('require')) {
293
+ $require = $block->getLayout()->createBlock('mana_core/require');
294
+ $html = $transport->getHtml();
295
+ $html = $require->toHtml(). $html;
296
+
297
+ // replace original content with wrapped one
298
+ $transport->setHtml($html);
299
+ }
300
+ }
301
+
302
+ /**
303
+ * If relevant global flag is raised, renders AJAX content into JSON response instead of typical full-page
304
+ * HTML response (handles event "controller_front_send_response_before")
305
+ * @param Varien_Event_Observer $observer
306
+ */
307
+ public function renderCustomResponse($observer) {
308
+ if ($callback = Mage::registry('m_response_callback')) {
309
+ call_user_func($callback);
310
+ }
311
+ }
312
+
313
+ /**
314
+ * REPLACE THIS WITH DESCRIPTION (handles event "controller_front_init_before")
315
+ * @param Varien_Event_Observer $observer
316
+ */
317
+ public function registerEarlyRouters($observer) {
318
+ /* @var $front Mage_Core_Controller_Varien_Front */
319
+ $front = $observer->getEvent()->getData('front');
320
+
321
+ foreach ($this->coreHelper()->getSortedXmlChildren(Mage::getConfig()->getNode('mana_core'), 'routers') as $key => $routerXml) {
322
+ $class = (string)$routerXml->class;
323
+ $front->addRouter($key, new $class);
324
+ }
325
+ }
326
+
327
+ /**
328
+ * REPLACE THIS WITH DESCRIPTION (handles event "controller_front_send_response_before")
329
+ * @param Varien_Event_Observer $observer
330
+ */
331
+ public function helpSavingInFullPageCache($observer) {
332
+ if ($queryParameters = Mage::registry('m_temporary_query_parameters')) {
333
+ foreach (array_keys($queryParameters) as $queryParameter) {
334
+ if (isset($_GET[$queryParameter])) {
335
+ unset($_GET[$queryParameter]);
336
+ }
337
+ }
338
+ }
339
+ }
340
+
341
+ #region Dependencies
342
+ /**
343
+ * @return Mana_Core_Helper_Data
344
+ */
345
+ public function coreHelper() {
346
+ return Mage::helper('mana_core');
347
+ }
348
+ #endregion
349
  }
350
 
app/code/local/Mana/Core/Model/Source/Js.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://www.manadev.com/license Proprietary License
7
+ */
8
+
9
+ /**
10
+ * Source for options of filter being filterable
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Source_Js extends Mana_Core_Model_Source_Abstract {
15
+ protected function _getAllOptions() {
16
+ /* @var $t Mana_Core_Helper_Data */
17
+ $t = Mage::helper(strtolower('Mana_Core'));
18
+ return array(
19
+ array('value' => 'min_ondemand', 'label' => $t->__('Load if required, minified version (recommended)')),
20
+ array('value' => 'min_everywhere', 'label' => $t->__('Load on all pages, minified version')),
21
+ array('value' => 'full_ondemand', 'label' => $t->__('Load if required, full version with comments')),
22
+ array('value' => 'full_everywhere', 'label' => $t->__('Load on all pages, full version with comments')),
23
+ array('value' => 'unload', 'label' => $t->__('Do not load')),
24
+ array('value' => 'skip', 'label' => $t->__('Take action as defined in theme layout XML files')),
25
+ );
26
+ }
27
+ }
app/code/local/Mana/Core/Model/Source/YesNoDefault.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
+ * Source for options of filter being filterable
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Source_YesNoDefault 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
+ array('value' => '', 'label' => Mage::helper('core')->__('Use Default')),
20
+ );
21
+ }
22
+ }
app/code/local/Mana/Core/Resource/Indexer.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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
+ abstract class Mana_Core_Resource_Indexer extends Mage_Core_Model_Mysql4_Abstract {
13
+ public function insert($tableName, $fields = array(), $onDuplicate = true) {
14
+ /* @var $core Mana_Core_Helper_Data */
15
+ $core = Mage::helper('mana_core');
16
+
17
+ return $core->insert($this->_getWriteAdapter(), $tableName, $fields, $onDuplicate);
18
+ }
19
+ }
app/code/local/Mana/Core/Rewrite/PageCache/Processor.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
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_Core_Rewrite_PageCache_Processor extends Enterprise_PageCache_Model_Processor {
13
+ protected function _getFullPageUrl() {
14
+ if ($url = Mage::registry('m_original_request_uri')) {
15
+ $uri = '';
16
+ if (isset($_SERVER['HTTP_HOST'])) {
17
+ $uri = $_SERVER['HTTP_HOST'];
18
+ } elseif (isset($_SERVER['SERVER_NAME'])) {
19
+ $uri = $_SERVER['SERVER_NAME'];
20
+ }
21
+ if ($uri) {
22
+ return $uri . $url;
23
+ }
24
+ }
25
+ return parent::_getFullPageUrl();
26
+ }
27
+ }
app/code/local/Mana/Core/etc/adminhtml.xml ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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://www.manadev.com/license Proprietary License
7
+ -->
8
+ <config>
9
+ <menu>
10
+ <mana>
11
+ <title>MANAdev</title>
12
+ <sort_order>79</sort_order>
13
+ <children>
14
+ <system_configuration translate="title" module="mana_core">
15
+ <title>Global Configuration</title>
16
+ <sort_order>20000</sort_order>
17
+ <children>
18
+ <mana translate="title" module="mana_core">
19
+ <title>General</title>
20
+ <action>adminhtml/system_config/edit/section/mana</action>
21
+ <sort_order>50</sort_order>
22
+ </mana>
23
+ </children>
24
+ </system_configuration>
25
+ </children>
26
+ </mana>
27
+ </menu>
28
+ <acl>
29
+ <resources>
30
+ <admin>
31
+ <children>
32
+ <mana translate="title" module="mana_core">
33
+ <title>Configure Mana Extensions</title>
34
+ <sort_order>79</sort_order>
35
+ <children>
36
+ <system_configuration translate="title" module="mana_core">
37
+ <title>Global Configuration</title>
38
+ <sort_order>20000</sort_order>
39
+ <children>
40
+ <mana translate="title" module="mana_core">
41
+ <title>General</title>
42
+ <sort_order>50</sort_order>
43
+ </mana>
44
+ </children>
45
+ </system_configuration>
46
+ </children>
47
+ </mana>
48
+ <system>
49
+ <children>
50
+ <config>
51
+ <children>
52
+ <mana translate="title" module="mana_core">
53
+ <title>MANAdev General Settings</title>
54
+ <sort_order>50050</sort_order>
55
+ </mana>
56
+ </children>
57
+ </config>
58
+ </children>
59
+ </system>
60
+ </children>
61
+ </admin>
62
+ </resources>
63
+ </acl>
64
+ </config>
app/code/local/Mana/Core/etc/config.xml CHANGED
@@ -12,7 +12,7 @@
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.12.21.19</version>
16
  </Mana_Core>
17
  </modules>
18
  <!-- This section contains module settings which are merged into global configuration during each page load,
@@ -27,11 +27,6 @@
27
  Mage::helper() calls. -->
28
  <class>Mana_Core_Helper</class>
29
  </mana_core>
30
- <catalog>
31
- <rewrite>
32
- <image>Mana_Core_Helper_Image</image>
33
- </rewrite>
34
- </catalog>
35
  </helpers>
36
  <!-- BASED ON SNIPPET: Blocks/Block support (config.xml) -->
37
  <!-- This section registers block classes to be accessible from layout XML files (in type="<block type>") or
@@ -70,6 +65,11 @@
70
  <!-- INSERT HERE: table-entity mappings -->
71
  </entities>
72
  </mana_core_resources>
 
 
 
 
 
73
  <!-- INSERT HERE: rewrites, ... -->
74
  </models>
75
  <!-- BASED ON SNIPPET: New Models/Event support (config.xml) -->
@@ -135,7 +135,65 @@
135
  </mana_core>
136
  </observers>
137
  </controller_action_layout_render_before>
138
- </events>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  <!-- BASED ON SNIPPET: Resources/Installation script support (config.xml) -->
140
  <!-- This tells Magento to analyze sql/mana_core_setup directory for install/upgrade scripts.
141
  Installation scripts should be named as 'mysql4-install-<new version>.php'.
@@ -174,7 +232,7 @@
174
 
175
  <!-- INSERT HERE: layout, translate, routers -->
176
  </frontend>
177
- <!-- BASED ON SNIPPET: Static Visuals/Adminhtml section (config.xml) -->
178
  <!-- This section enables static visual changes in admin area. -->
179
  <adminhtml>
180
  <!-- BASED ON SNIPPET: Translation support/Adminhtml (config.xml) -->
@@ -201,12 +259,54 @@
201
  </layout>
202
  <!-- INSERT HERE: layout, translate, routers -->
203
  </adminhtml>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  <!-- INSERT HERE: adminhtml, frontend, ... -->
205
- <default>
206
  <mana_dev>
207
  <debug>
208
  <jquery_min>1</jquery_min>
209
  </debug>
210
  </mana_dev>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  </default>
212
  </config>
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>13.10.17.19</version>
16
  </Mana_Core>
17
  </modules>
18
  <!-- This section contains module settings which are merged into global configuration during each page load,
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
65
  <!-- INSERT HERE: table-entity mappings -->
66
  </entities>
67
  </mana_core_resources>
68
+ <enterprise_pagecache>
69
+ <rewrite>
70
+ <processor>Mana_Core_Rewrite_PageCache_Processor</processor>
71
+ </rewrite>
72
+ </enterprise_pagecache>
73
  <!-- INSERT HERE: rewrites, ... -->
74
  </models>
75
  <!-- BASED ON SNIPPET: New Models/Event support (config.xml) -->
135
  </mana_core>
136
  </observers>
137
  </controller_action_layout_render_before>
138
+ <controller_action_layout_generate_blocks_before><!-- this is event name this module listens for -->
139
+ <observers>
140
+ <mana_core>
141
+ <class>mana_core/observer</class>
142
+ <!-- model name of class containing event handler methods -->
143
+ <method>processJsCssFiles</method>
144
+ <!-- event handler method name -->
145
+ </mana_core>
146
+ </observers>
147
+ </controller_action_layout_generate_blocks_before>
148
+ <core_block_abstract_to_html_after><!-- this is event name this module listens for -->
149
+ <observers>
150
+ <mana_core>
151
+ <class>mana_core/observer</class>
152
+ <!-- model name of class containing event handler methods -->
153
+ <method>addClientSideBlockMarkup</method>
154
+ <!-- event handler method name -->
155
+ </mana_core>
156
+ </observers>
157
+ </core_block_abstract_to_html_after>
158
+ <core_block_abstract_to_html_after><!-- this is event name this module listens for -->
159
+ <observers>
160
+ <mana_core_require>
161
+ <class>mana_core/observer</class>
162
+ <!-- model name of class containing event handler methods -->
163
+ <method>renderRequireSettings</method>
164
+ <!-- event handler method name -->
165
+ </mana_core_require>
166
+ </observers>
167
+ </core_block_abstract_to_html_after>
168
+ <controller_front_send_response_before><!-- this is event name this module listens for -->
169
+ <observers>
170
+ <mana_core>
171
+ <class>mana_core/observer</class>
172
+ <!-- model name of class containing event handler methods -->
173
+ <method>renderCustomResponse</method>
174
+ <!-- event handler method name -->
175
+ </mana_core>
176
+ </observers>
177
+ </controller_front_send_response_before>
178
+ <controller_front_init_before><!-- this is event name this module listens for -->
179
+ <observers>
180
+ <mana_core>
181
+ <class>mana_core/observer</class>
182
+ <!-- model name of class containing event handler methods -->
183
+ <method>registerEarlyRouters</method>
184
+ <!-- event handler method name -->
185
+ </mana_core>
186
+ </observers>
187
+ </controller_front_init_before>
188
+ <controller_front_send_response_before>
189
+ <observers>
190
+ <enterprise_pagecache>
191
+ <class>mana_core/observer</class>
192
+ <method>helpSavingInFullPageCache</method>
193
+ </enterprise_pagecache>
194
+ </observers>
195
+ </controller_front_send_response_before>
196
+ </events>
197
  <!-- BASED ON SNIPPET: Resources/Installation script support (config.xml) -->
198
  <!-- This tells Magento to analyze sql/mana_core_setup directory for install/upgrade scripts.
199
  Installation scripts should be named as 'mysql4-install-<new version>.php'.
232
 
233
  <!-- INSERT HERE: layout, translate, routers -->
234
  </frontend>
235
+ <!-- BASED ON SNIPPET: Static Visuals/Adminhtml section (config.xml) -->
236
  <!-- This section enables static visual changes in admin area. -->
237
  <adminhtml>
238
  <!-- BASED ON SNIPPET: Translation support/Adminhtml (config.xml) -->
259
  </layout>
260
  <!-- INSERT HERE: layout, translate, routers -->
261
  </adminhtml>
262
+ <mana_core>
263
+ <js>
264
+ <!--<require module="mana_core">
265
+ <type>library</type>
266
+ <skinnable>0</skinnable>
267
+ <full_path>jquery/require.js</full_path>
268
+ <minified_path>jquery/require.min.js</minified_path>
269
+ <sort_order>50</sort_order>
270
+ </require>-->
271
+ <jquery module="mana_core">
272
+ <type>library</type>
273
+ <skinnable>0</skinnable>
274
+ <full_path>jquery/jquery.js</full_path>
275
+ <minified_path>jquery/jquery.min.js</minified_path>
276
+ <sort_order>100</sort_order>
277
+ </jquery>
278
+ </js>
279
+ <page_types>
280
+ <category><helper>mana_core/pageType_category</helper></category>
281
+ <cms_page><helper>mana_core/pageType_cmsPage</helper></cms_page>
282
+ <home_page><helper>mana_core/pageType_homePage</helper></home_page>
283
+ <search><helper>mana_core/pageType_search</helper></search>
284
+ </page_types>
285
+ </mana_core>
286
  <!-- INSERT HERE: adminhtml, frontend, ... -->
287
+ <default>
288
  <mana_dev>
289
  <debug>
290
  <jquery_min>1</jquery_min>
291
  </debug>
292
  </mana_dev>
293
+ <mana>
294
+ <js>
295
+ <!--<require>min_ondemand</require>-->
296
+ <jquery>min_ondemand</jquery>
297
+ </js>
298
+ <seo_symbols>
299
+ <dash>--to-</dash>
300
+ <space>-</space>
301
+ <slash>--per-</slash>
302
+ <plus>--plus-</plus>
303
+ <underscore>--uscore-</underscore>
304
+ <quote>--qt-</quote>
305
+ <double_quote>--dqt-</double_quote>
306
+ <hash>--no-</hash>
307
+ <percent>--percent-</percent>
308
+ <ampersand>--and-</ampersand>
309
+ </seo_symbols>
310
+ </mana>
311
  </default>
312
  </config>
app/code/local/Mana/Core/etc/system.xml ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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://www.manadev.com/license Proprietary License
7
+ -->
8
+ <config>
9
+ <tabs>
10
+ <mana>
11
+ <label>MANAdev</label>
12
+ <sort_order>250</sort_order>
13
+ </mana>
14
+ </tabs>
15
+ <sections>
16
+ <mana translate="label" module="mana_core">
17
+ <label>General</label>
18
+ <tab>mana</tab>
19
+ <frontend_type>text</frontend_type>
20
+ <sort_order>50</sort_order>
21
+ <show_in_default>1</show_in_default>
22
+ <show_in_website>1</show_in_website>
23
+ <show_in_store>1</show_in_store>
24
+ <groups>
25
+ <js translate="label" module="mana_core">
26
+ <label>JavaScript Libraries</label>
27
+ <frontend_type>text</frontend_type>
28
+ <sort_order>10000</sort_order>
29
+ <show_in_default>1</show_in_default>
30
+ <show_in_website>1</show_in_website>
31
+ <show_in_store>1</show_in_store>
32
+ <fields>
33
+ <!--<require translate="label">
34
+ <label>RequireJS (version 2.1.4)</label>
35
+ <frontend_type>select</frontend_type>
36
+ <source_model>mana_core/source_js</source_model>
37
+ <sort_order>50</sort_order>
38
+ <show_in_default>1</show_in_default>
39
+ <show_in_website>1</show_in_website>
40
+ <show_in_store>1</show_in_store>
41
+ </require>-->
42
+ <jquery translate="label comment">
43
+ <label>jQuery (version 1.8.3)</label>
44
+ <comment>Always runs in jQuery.noConflict() mode (slight modification) to prevent conflicts with Prototype JavaScript library used by Magento.</comment>
45
+ <frontend_type>select</frontend_type>
46
+ <source_model>mana_core/source_js</source_model>
47
+ <sort_order>100</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ <show_in_store>1</show_in_store>
51
+ </jquery>
52
+ </fields>
53
+ </js>
54
+ </groups>
55
+ </mana>
56
+ </sections>
57
+ </config>
app/code/local/Mana/Db/Exception/Formula.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Exception_Formula extends Exception {
13
+ public function addMessage($message) {
14
+ $this->message .= $message;
15
+ }
16
+ }
app/code/local/Mana/Db/Exception/Validation.php CHANGED
@@ -1,11 +1,24 @@
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
  }
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
+ * @author Mana Team
10
+ *
11
+ */
12
  class Mana_Db_Exception_Validation extends Exception {
13
  protected $_errors;
14
  public function __construct($errors) {
15
  $this->_errors = $errors;
16
  }
17
+
18
+ /**
19
+ * @return array
20
+ */
21
+ public function getErrors() {
22
  return $this->_errors;
23
  }
24
  }
app/code/local/Mana/Db/Helper/Config.php ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Config extends Mage_Core_Helper_Abstract {
13
+ const ROLE_PRIMARY_KEY = 'primary_key';
14
+ const ROLE_DEFAULT_VALUE = 'default_value';
15
+ const ROLE_STORE_SPECIFICS = 'store_specifics';
16
+ const ROLE_GRID_EDITING = 'grid_editing';
17
+ const ROLE_FLAT = 'flat';
18
+
19
+ const MODULE_LEVEL = 0;
20
+ const ENTITY_LEVEL = 1;
21
+ const SCOPE_LEVEL = 2;
22
+ const FIELD_LEVEL = 3;
23
+
24
+ protected $_xml;
25
+ protected $_scopeXml = array();
26
+ protected $_configLevels = array(
27
+ self::MODULE_LEVEL => 'module',
28
+ self::ENTITY_LEVEL => 'entity',
29
+ self::SCOPE_LEVEL => 'scope',
30
+ self::FIELD_LEVEL => 'field'
31
+ );
32
+
33
+ /**
34
+ * @param string $setupVersion
35
+ * @return Mana_Db_Model_Setup_Abstract
36
+ * @throws Exception
37
+ */
38
+ public function getSetup($setupVersion) {
39
+ // $setupVersion = $this->getXml()->getNode("modules/{$moduleName}/scripts/v{$version}");
40
+ // if (empty($setupVersion->installer)) {
41
+ // throw new Exception($this->__("Setup version not defined for module '%s' upgrade script %s",
42
+ // $moduleName, $version));
43
+ // }
44
+
45
+ $setupVersion = 'mana_db/setup_v' . str_replace('.', '', $setupVersion);
46
+ if (!($setupVersion = Mage::getModel($setupVersion))) {
47
+ throw new Exception($this->__("Setup version %s not found", $setupVersion));
48
+ }
49
+
50
+ return $setupVersion;
51
+ }
52
+
53
+ /**
54
+ * Load XML config from m_db.xml files and caches it
55
+ *
56
+ * @return Varien_Simplexml_Config
57
+ */
58
+ public function getXml() {
59
+ if (!$this->_xml) {
60
+ $cachedXml = Mage::app()->loadCache('m_db_config');
61
+ if ($cachedXml) {
62
+ $this->_xml = new Varien_Simplexml_Config($cachedXml);
63
+ }
64
+ else {
65
+ $config = new Varien_Simplexml_Config();
66
+ $config->loadString('<?xml version="1.0"?><config></config>');
67
+ Mage::getConfig()->loadModulesConfiguration('m_db.xml', $config);
68
+ $this->_xml = $config;
69
+ $this->_prepareXml();
70
+ if (Mage::app()->useCache('config')) {
71
+ Mage::app()->saveCache($config->getXmlString(), 'm_db_config', array(Mage_Core_Model_Config::CACHE_TAG));
72
+ }
73
+ }
74
+ }
75
+
76
+ return $this->_xml;
77
+ }
78
+
79
+ protected function _prepareXml() {
80
+ $this->iterate(array(
81
+ 'module' => array($this, '_prepareModuleXml'),
82
+ 'entity' => array($this, '_prepareEntityXml'),
83
+ 'scope' => array($this, '_prepareScopeXml'),
84
+ 'field' => array($this, '_prepareFieldXml'),
85
+ ));
86
+
87
+ $this->iterate(array('module' => array($this, '_prepareCustom')));
88
+
89
+ $this->iterate(array(
90
+ 'module' => array($this, '_prepareModuleXml'),
91
+ 'entity' => array($this, '_prepareEntityXml'),
92
+ 'scope' => array($this, '_prepareScopeXml'),
93
+ 'field' => array($this, '_prepareFieldXml'),
94
+ ));
95
+ }
96
+
97
+ /**
98
+ * @param Varien_Object $context
99
+ * @param Varien_Simplexml_Element $module
100
+ */
101
+ protected function _prepareModuleXml($context, $module) {
102
+ $name = $module->getName();
103
+ if (!isset($module['module'])) {
104
+ $module['module'] = $name;
105
+ }
106
+ $this->propagateName($module);
107
+ }
108
+
109
+ /**
110
+ * @param Varien_Object $context
111
+ * @param Varien_Simplexml_Element $module
112
+ * @param Varien_Simplexml_Element $entity
113
+ */
114
+ protected function _prepareEntityXml($context, $module, $entity) {
115
+ $this->propagateAttributes($module, $entity, array('module', 'version'));
116
+ if (empty($entity->scopes)) {
117
+ $entity->scopes->global->name = 'global';
118
+ }
119
+ $this->propagateName($entity);
120
+ }
121
+
122
+ /**
123
+ * @param Varien_Object $context
124
+ * @param Varien_Simplexml_Element $module
125
+ * @param Varien_Simplexml_Element $entity
126
+ * @param Varien_Simplexml_Element $scope
127
+ */
128
+ protected function _prepareScopeXml($context, $module, $entity, $scope) {
129
+ $this->propagateAttributes($entity, $scope, array('module', 'version'));
130
+ foreach ($entity->children() as $child) {
131
+ if ($child->getName() != 'scopes') {
132
+ $scope->extendChild($child);
133
+ }
134
+ }
135
+ if (!empty($scope->unique)) {
136
+ /** @noinspection PhpParamsInspection */
137
+ $this->propagateAttributes($scope, $scope->unique, array('module', 'version'));
138
+ foreach ($scope->unique->children() as $index) {
139
+ /** @noinspection PhpParamsInspection */
140
+ $this->propagateAttributes($scope->unique, $index, array('module', 'version'));
141
+ foreach ($index->children() as $column) {
142
+ $this->propagateAttributes($index, $column, array('module', 'version'));
143
+ }
144
+ }
145
+ }
146
+ $this->propagateName($scope);
147
+ }
148
+
149
+ /**
150
+ * @param Varien_Object $context
151
+ * @param Varien_Simplexml_Element $module
152
+ * @param Varien_Simplexml_Element $entity
153
+ * @param Varien_Simplexml_Element $scope
154
+ * @param Varien_Simplexml_Element $field
155
+ */
156
+ protected function _prepareFieldXml($context, $module, $entity, $scope, $field) {
157
+ $this->propagateAttributes($scope, $field, array('module', 'version'));
158
+ $this->propagateName($field);
159
+ }
160
+
161
+ /**
162
+ * @param Varien_Object $context
163
+ * @param Varien_Simplexml_Element $module
164
+ */
165
+ protected function _prepareCustom($context, $module) {
166
+ if (!$module->installer_versions) {
167
+ return;
168
+ }
169
+
170
+ foreach ($module->installer_versions->children() as $moduleVersion => $version) {
171
+ $version = (string) $version;
172
+
173
+ $setupVersion = $this->getSetup((string)$version);
174
+ $setupVersion
175
+ ->setModuleName((string)$module->name)
176
+ ->setVersion(substr($moduleVersion, 1))
177
+ ->prepare();
178
+ }
179
+ }
180
+ /**
181
+ * @param Varien_Simplexml_Element $source
182
+ * @param Varien_Simplexml_Element $target
183
+ * @param string[] $attributes
184
+ */
185
+ public function propagateAttributes($source, $target, $attributes) {
186
+ foreach ($attributes as $attribute) {
187
+ if (!isset($target[$attribute]) && isset($source[$attribute])) {
188
+ $target[$attribute] = (string)$source[$attribute];
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @param Varien_Simplexml_Element $target
195
+ */
196
+ public function propagateName($target) {
197
+ $target->name = $target->getName();
198
+ }
199
+
200
+ /**
201
+ * @param callable[] $callbacks
202
+ */
203
+ public function iterate($callbacks) {
204
+ $args = array(new Varien_Object());
205
+ $config = $this->_xml ? $this->_xml->getNode() : $this->getXml()->getNode();
206
+ $this->_iterateLevel(self::MODULE_LEVEL, $config->modules, $callbacks, $args,
207
+ array($this, '_iterateEntities'));
208
+ return $args[0];
209
+ }
210
+
211
+ protected function _iterateLevel ($level, $elements, $callbacks, $args, $deeperLevelIterator = null) {
212
+ if (!$elements) {
213
+ return;
214
+ }
215
+ $hasCallbacks = false;
216
+ foreach ($this->_configLevels as $levelIndex =>$levelKey) {
217
+ if ($levelIndex < $level) {
218
+ continue;
219
+ }
220
+ $intersection = array_intersect(array("{$levelKey}_before", $levelKey, "{$levelKey}_after"),
221
+ array_keys($callbacks));
222
+ if (!empty($intersection)) {
223
+ $hasCallbacks = true;
224
+ break;
225
+ }
226
+ }
227
+ if (!$hasCallbacks) {
228
+ return;
229
+ }
230
+
231
+ $callbackKey = $this->_configLevels[$level];
232
+ foreach ($elements->children() as $config) {
233
+ $argsToBePassed = array_merge($args, array($config));
234
+ if (isset($callbacks["{$callbackKey}_before"])) {
235
+ call_user_func_array($callbacks["{$callbackKey}_before"], $argsToBePassed);
236
+ }
237
+ if (isset($callbacks[$callbackKey])) {
238
+ call_user_func_array($callbacks[$callbackKey], $argsToBePassed);
239
+ }
240
+ if ($deeperLevelIterator) {
241
+ call_user_func($deeperLevelIterator, $config, $callbacks, $argsToBePassed);
242
+ }
243
+ if (isset($callbacks["{$callbackKey}_after"])) {
244
+ call_user_func_array($callbacks["{$callbackKey}_after"], $argsToBePassed);
245
+ }
246
+ }
247
+ }
248
+ protected function _iterateEntities($config, $callbacks, $args) {
249
+ $this->_iterateLevel(self::ENTITY_LEVEL, $config->entities, $callbacks, $args,
250
+ array($this, '_iterateScopes'));
251
+ }
252
+ protected function _iterateScopes($config, $callbacks, $args) {
253
+ $this->_iterateLevel(self::SCOPE_LEVEL, $config->scopes, $callbacks, $args,
254
+ array($this, '_iterateFields'));
255
+ }
256
+ protected function _iterateFields($config, $callbacks, $args) {
257
+ $this->_iterateLevel(self::FIELD_LEVEL, $config->fields, $callbacks, $args);
258
+ }
259
+
260
+ /**
261
+ * @param string $entityName
262
+ * @return Varien_Simplexml_Element | bool
263
+ */
264
+ public function getEntityXml($entityName) {
265
+ $xml = $this->getXml();
266
+ $parts = explode('/', $entityName);
267
+ list($module, $entity) = $parts;
268
+ $entityXml = $xml->getXpath("//modules/$module/entities/$entity");
269
+
270
+ return empty($entityXml) ? false : $entityXml[0];
271
+ }
272
+
273
+ public function getTableXml($entityName) {
274
+ $xml = $this->getXml();
275
+ $parts = explode('/', $entityName);
276
+ list($module, $entity) = $parts;
277
+ $entityXml = $xml->getXpath("//modules/$module/tables/$entity");
278
+
279
+ return empty($entityXml) ? false : $entityXml[0];
280
+ }
281
+
282
+
283
+ /**
284
+ * @param string $fullEntityName
285
+ * @return Varien_Simplexml_Element | bool
286
+ */
287
+ public function getScopeXml($fullEntityName) {
288
+ if (!isset($this->_scopeXml[$fullEntityName])) {
289
+ $xml = $this->getXml();
290
+ $parts = explode('/', $fullEntityName);
291
+ if (count($parts) > 2) {
292
+ list($module, $entity, $scope) = $parts;
293
+ }
294
+ else {
295
+ list($module, $entity) = $parts;
296
+ $scope = 'global';
297
+ }
298
+ $scopeXml = $xml->getXpath("//modules/$module/entities/$entity/scopes/$scope");
299
+ if (empty($scopeXml) && $scope == 'global') {
300
+ $scopeXml = $xml->getXpath("//modules/$module/entities/$entity");
301
+ }
302
+
303
+ $this->_scopeXml[$fullEntityName] = empty($scopeXml) ? false : $scopeXml[0];
304
+ }
305
+ return $this->_scopeXml[$fullEntityName];
306
+ }
307
+
308
+ /**
309
+ * @param string $entity
310
+ * @param string $field
311
+ * @return Varien_Simplexml_Element | bool
312
+ */
313
+ public function getFieldXml($entity, $field) {
314
+ $scopeXml = $this->getScopeXml($entity);
315
+
316
+ $resultXml = $scopeXml->xpath("fields/$field");
317
+ return empty($resultXml) ? false : $resultXml[0];
318
+ }
319
+
320
+ public function getForeignKey($parentEntity, $childEntity) {
321
+ /* @var $db Mana_Db_Helper_Data */
322
+ $db = Mage::helper('mana_db');
323
+
324
+ $scopeXml = $this->getScopeXml($childEntity);
325
+
326
+ $resultXml = $scopeXml->xpath("fields/*[foreign/entity='$parentEntity' or ".
327
+ "foreign/entity='{$parentEntity}/global']");
328
+ if (empty($resultXml)) {
329
+ return false;
330
+ }
331
+
332
+ /* @var $fieldXml Varien_Simplexml_Element */
333
+ $fieldXml = $resultXml[0];
334
+ return $fieldXml->getName();
335
+ }
336
+
337
+ /**
338
+ * @param string $entity
339
+ * @return Varien_Simplexml_Element[]
340
+ */
341
+ public function getForeignXmls($entity) {
342
+ /* @var $db Mana_Db_Helper_Data */
343
+ $db = Mage::helper('mana_db');
344
+
345
+ $scopeXml = $this->getScopeXml($entity);
346
+
347
+ return $scopeXml->xpath("fields/*[foreign]");
348
+ }
349
+
350
+ /**
351
+ * @param string $entity
352
+ * @return Varien_Simplexml_Element[]
353
+ */
354
+ public function getScopeValidators($entity) {
355
+ $scopeXml = $this->getScopeXml($entity);
356
+ return $scopeXml->xpath("validation/*");
357
+ }
358
+
359
+ /**
360
+ * @param string $entity
361
+ * @return Varien_Simplexml_Element[]
362
+ */
363
+ public function getScopeFields($entity) {
364
+ $scopeXml = $this->getScopeXml($entity);
365
+
366
+ return $scopeXml->xpath("fields/*");
367
+ }
368
+
369
+ /**
370
+ * @param string $entity
371
+ * @param string $field
372
+ * @return Varien_Simplexml_Element || bool
373
+ */
374
+ public function getScopeField($entity, $field) {
375
+ $scopeXml = $this->getScopeXml($entity);
376
+
377
+ /** @noinspection PhpUndefinedFieldInspection */
378
+ $result = $scopeXml->fields->$field;
379
+ return empty($result) ? false : $result;
380
+ }
381
+
382
+ /**
383
+ * @param string $entity
384
+ * @param string $field
385
+ * @return Varien_Simplexml_Element[]
386
+ */
387
+ public function getFieldValidators($entity, $field = null) {
388
+ $fieldXml = $entity instanceof Varien_Simplexml_Element ? $entity : $this->getFieldXml($entity, $field);
389
+
390
+ return $fieldXml->xpath("validation/*");
391
+ }
392
+
393
+ /**
394
+ * @param string $entity
395
+ * @return Varien_Simplexml_Element[]
396
+ */
397
+ public function getScopePostValidators($entity) {
398
+ $scopeXml = $this->getScopeXml($entity);
399
+
400
+ return $scopeXml->xpath("post_validation/*");
401
+ }
402
+
403
+ /**
404
+ * @param string $entity
405
+ * @param string $field
406
+ * @return Varien_Simplexml_Element[]
407
+ */
408
+ public function getFieldPostValidators($entity, $field = null) {
409
+ $fieldXml = $entity instanceof Varien_Simplexml_Element ? $entity : $this->getFieldXml($entity, $field);
410
+
411
+ return $fieldXml->xpath("post_validation/*");
412
+ }
413
+
414
+ }
app/code/local/Mana/Db/Helper/Data.php CHANGED
@@ -11,9 +11,36 @@
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()) {
@@ -21,7 +48,7 @@ class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
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
  }
@@ -47,7 +74,7 @@ class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
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
  }
@@ -102,11 +129,17 @@ class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
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
  }
@@ -136,89 +169,93 @@ class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
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
  }
@@ -327,6 +364,9 @@ class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
327
  return $a < $b ? -1 : 1;
328
  }
329
 
 
 
 
330
  protected $_inEditing = false;
331
  public function getInEditing() {
332
  return $this->_inEditing;
@@ -360,4 +400,182 @@ class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
360
  'deleted' => array(),
361
  );
362
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  }
11
  * @author Mana Team
12
  */
13
  class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
14
+ protected $_logQueries = false;
15
+ protected $_resourceSingletons = array();
16
+
17
  public function getLogQueries() {
18
+ return Mage::getStoreConfig('mana_db/replicate/log_queries');
19
+ }
20
+ public function getLogActions() {
21
+ return Mage::getStoreConfig('mana_db/replicate/log_actions');
22
+ }
23
+ public function getSkipInserts() {
24
+ return Mage::getStoreConfig('mana_db/replicate/skip_inserts');
25
+ }
26
+ public function getSkipUpdates() {
27
+ return Mage::getStoreConfig('mana_db/replicate/skip_updates');
28
+ }
29
+ public function getSkipDeletes() {
30
+ return Mage::getStoreConfig('mana_db/replicate/skip_deletes');
31
+ }
32
+ public function getBatchSize() {
33
+ $result = Mage::getStoreConfig('mana_db/replicate/batch_size');
34
+ return $result ? $result : 10000;
35
+ }
36
+ public function getNoTransaction() {
37
+ return Mage::getStoreConfig('mana_db/replicate/no_transaction');
38
+ }
39
+ public function getMaxExecutionTime() {
40
+ return Mage::getStoreConfig('mana_db/replicate/max_execution_time');
41
+ }
42
+ public function getMemoryLimit() {
43
+ return Mage::getStoreConfig('mana_db/replicate/memory_limit');
44
  }
45
  public function logQuery($action, $query) {
46
  if ($this->getLogQueries()) {
48
  }
49
  }
50
  public function logAction($action, $object) {
51
+ if ($this->getLogActions()) {
52
  Mage::log($action.': '.$object->toJson(), Zend_Log::DEBUG, 'replicate.log');
53
  }
54
  }
74
  }
75
  public function joinLeft($select, $alias, $table, $condition) {
76
  if (!array_key_exists($alias, $select->getPart(Zend_Db_Select::FROM))) {
77
+ $select->joinLeft(array($alias => $table), $condition, null);
78
  }
79
  return $select;
80
  }
129
  $options = array_merge(array(
130
  'db' => Mage::getSingleton('core/resource')->getConnection('core/write'),
131
  'trackKeys' => false,
132
+ 'transaction' => !$this->getNoTransaction(),
133
  'filter' => array(),
134
+ 'batchSize' => $this->getBatchSize(),
135
+ 'object' => null,
136
  ), $options);
137
+ if ($this->getMaxExecutionTime()) {
138
+ ini_set('max_execution_time', $this->getMaxExecutionTime());
139
+ }
140
+ if ($this->getMemoryLimit()) {
141
+ ini_set('memory_limit', $this->getMemoryLimit());
142
+ }
143
  if (count($options['filter']) == 0) {
144
  $options['db']->resetDdlCache();
145
  }
169
  $model = Mage::getResourceSingleton($entityName);
170
 
171
  // update existing rows
172
+ if (!$this->getSkipUpdates()) {
173
+ $target->setSelects(array())->setIsKeyFilterApplied(false);
174
+ $model->prepareReplicationUpdateSelects($target, $options);
175
+ if (count($target->getSelects()) && (!$options['trackKeys'] || $target->getIsKeyFilterApplied())) {
176
+ foreach ($target->getSelects() as $select) {
177
+ $offset = 0;
178
+ $this->logQuery('UPDATE', $select);
179
+ do {
180
+ $sourceData = $options['db']->fetchAll($select->limit($options['batchSize'], $offset));
181
+ if ($sourceData && count($sourceData)) {
182
+ foreach ($sourceData as $values) {
183
+ if ($object = $model->processReplicationUpdate($values, $options)) {
184
+ if ($object != $options['object']) {
185
+ $this->logAction('UPDATE', $object);
186
+ $object->save();
187
+ if ($options['trackKeys']) {
188
+ $target->setSavedKey($object->getId(), $object->getId());
189
+ }
190
+ }
191
+ else {
192
+ $object->unsetData('_m_prevent_replication');
193
+ }
194
+ }
195
+ }
196
+ }
197
+ $offset += $options['batchSize'];
198
+ } while ($sourceData && count($sourceData));
199
+ }
200
+ }
201
  }
 
202
  // insert rows
203
+ if (!$this->getSkipInserts()) {
204
+ $target->setSelects(array())->setIsKeyFilterApplied(false);
205
+ $model->prepareReplicationInsertSelects($target, $options);
206
+ if (count($target->getSelects()) && (!$options['trackKeys'] || $target->getIsKeyFilterApplied())) {
207
+ foreach ($target->getSelects() as $select) {
208
+ $offset = 0;
209
+ $this->logQuery('INSERT', $select);
210
+ do {
211
+ $sourceData = $options['db']->fetchAll($select->limit($options['batchSize']));
212
+ if ($sourceData && count($sourceData)) {
213
+ foreach ($sourceData as $values) {
214
+ if ($object = $model->processReplicationInsert($values, $options)) {
215
+ if ($object != $options['object']) {
216
+ $this->logAction('INSERT', $object);
217
+ $object->save();
218
+ if ($options['trackKeys']) {
219
+ $target->setSavedKey($object->getId(), $object->getId());
220
+ }
221
+ }
222
+ else {
223
+ $object->unsetData('_m_prevent_replication');
224
+ }
225
+ }
226
+ }
227
+ }
228
+ $offset += $options['batchSize'];
229
+ } while ($sourceData && count($sourceData));
230
+ }
231
+ }
232
  }
 
233
  // delete rows
234
+ if (!$this->getSkipDeletes()) {
235
+ $target->setSelects(array())->setIsKeyFilterApplied(false);
236
+ $model->prepareReplicationDeleteSelects($target, $options);
237
+ if (count($target->getSelects()) && (!$options['trackKeys'] || $target->getIsKeyFilterApplied())) {
238
+ foreach ($target->getSelects() as $select) {
239
+ $offset = 0;
240
+ $this->logQuery('DELETE', $select);
241
+ do {
242
+ $ids = $options['db']->fetchCol($select->limit($options['batchSize']));
243
+ if ($ids && count($ids)) {
244
+ $model->processReplicationDelete($ids, $options);
245
+ if ($options['trackKeys']) {
246
+ foreach ($ids as $id) {
247
+ $target->setDeletedKey($id, $id);
248
+ }
249
+ }
250
+ }
251
+ $offset += $options['batchSize'];
252
+ } while ($ids && count($ids));
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
  if ($options['transaction']) {
260
  $options['db']->commit();
261
  }
364
  return $a < $b ? -1 : 1;
365
  }
366
 
367
+ public function isEditingSessionExpired($editSessionId) {
368
+ return Mage::getResourceSingleton('mana_db/edit_session')->isExpired($editSessionId);
369
+ }
370
  protected $_inEditing = false;
371
  public function getInEditing() {
372
  return $this->_inEditing;
400
  'deleted' => array(),
401
  );
402
  }
403
+
404
+ protected $_resourceSuffixes = array('_collection');
405
+ public function getSuffix ($entityName, $suffixes) {
406
+ /* @var $core Mana_Core_Helper_Data */
407
+ $core = Mage::helper(strtolower('Mana_Core'));
408
+
409
+ foreach ($suffixes as $candidateSuffix) {
410
+ if ($core->endsWith($entityName, $candidateSuffix)) {
411
+ return $candidateSuffix;
412
+ }
413
+ }
414
+ return '';
415
+ }
416
+ public function getScopedName($entityName) {
417
+ if ($suffix = $this->getSuffix($entityName, $this->_resourceSuffixes)) {
418
+ $entityName = substr($entityName, 0, strlen($entityName) - strlen($suffix));
419
+ }
420
+
421
+ $parts = explode('/', $entityName);
422
+ if (count($parts) > 2) {
423
+ if ($parts[2] == 'global') {
424
+ $entityName = "{$parts[0]}/{$parts[1]}";
425
+ }
426
+ else {
427
+ $entityName = "{$parts[0]}/{$parts[1]}_{$parts[2]}";
428
+ }
429
+ }
430
+ return $entityName . $suffix;
431
+ }
432
+
433
+ /**
434
+ * @param string $entityName
435
+ * @param array|null $arguments
436
+ * @return Mana_Db_Resource_Entity_Collection | Mana_Db_Resource_Entity
437
+ */
438
+ public function getResourceModel($entityName, $arguments = null) {
439
+ if ($suffix = $this->getSuffix($entityName, $this->_resourceSuffixes)) {
440
+ $entityName = substr($entityName, 0, strlen($entityName) - strlen($suffix));
441
+ }
442
+
443
+ $arguments = array_merge(array(
444
+ 'scope' => $entityName,
445
+ ), $arguments ? (is_array($arguments) ? $arguments : array('resource' => $arguments)) : array());
446
+
447
+
448
+ $arguments = array_merge(array('scope' => $entityName), $arguments);
449
+
450
+ $resolvedEntityName = $this->getScopedName($entityName);
451
+ if ($this->resourceExists($resolvedEntityName . $suffix)) {
452
+ return Mage::getResourceModel($resolvedEntityName . $suffix, $arguments);
453
+ }
454
+
455
+ /* @var $dbConfig Mana_Db_Helper_Config */
456
+ $dbConfig = Mage::helper('mana_db/config');
457
+ if (!($scopeXml = $dbConfig->getScopeXml($entityName))) {
458
+ return Mage::getResourceModel('mana_db/entity' . $suffix, $arguments);
459
+ }
460
+
461
+ if (!empty($scopeXml->flattens)) {
462
+ $entityName = (string)$scopeXml->flattens;
463
+ $resolvedEntityName = $this->getScopedName($entityName);
464
+ if ($this->resourceExists($resolvedEntityName . $suffix)) {
465
+ return Mage::getResourceModel($resolvedEntityName . $suffix, $arguments);
466
+ }
467
+ $scopeXml = $dbConfig->getScopeXml($entityName);
468
+ }
469
+ if (!empty($scopeXml->store_specifics_for)) {
470
+ $entityName = (string)$scopeXml->store_specifics_for;
471
+ $resolvedEntityName = $this->getScopedName($entityName);
472
+ if ($this->resourceExists($resolvedEntityName . $suffix)) {
473
+ return Mage::getResourceModel($resolvedEntityName . $suffix, $arguments);
474
+ }
475
+ }
476
+
477
+ return Mage::getResourceModel('mana_db/entity' . $suffix, $arguments);
478
+ }
479
+
480
+ /**
481
+ * @param string $entityName
482
+ * @param array | null $arguments
483
+ * @return Mana_Db_Resource_Entity
484
+ */
485
+ public function getResourceSingleton($entityName, $arguments = null) {
486
+ if ($suffix = $this->getSuffix($entityName, $this->_resourceSuffixes)) {
487
+ $entityNameWithoutSuffix = substr($entityName, 0, strlen($entityName) - strlen($suffix));
488
+ }
489
+ else {
490
+ $entityNameWithoutSuffix = $entityName;
491
+ }
492
+
493
+ $arguments = array_merge(array(
494
+ 'scope' => $entityNameWithoutSuffix,
495
+ ), $arguments ? (is_array($arguments) ? $arguments : array('resource' => $arguments)) : array());
496
+
497
+ $resolvedEntityName = $this->getScopedName($entityName);
498
+ if ($this->resourceExists($resolvedEntityName)) {
499
+ return Mage::getResourceSingleton($resolvedEntityName, $arguments);
500
+ }
501
+ else {
502
+ if (!isset($this->_resourceSingletons[$resolvedEntityName])) {
503
+ $this->_resourceSingletons[$resolvedEntityName] = $this->getResourceModel($entityName, $arguments);
504
+ }
505
+ return $this->_resourceSingletons[$resolvedEntityName];
506
+ }
507
+ }
508
+ /**
509
+ * @param string $entityName
510
+ * @param array $arguments
511
+ * @return Mana_Db_Model_Entity
512
+ */
513
+ public function getModel($entityName, $arguments = array()) {
514
+ $arguments = array_merge(array('scope' => $entityName), $arguments);
515
+
516
+ $resolvedEntityName = $this->getScopedName($entityName);
517
+ if ($this->modelExists($resolvedEntityName)) {
518
+ return Mage::getModel($resolvedEntityName, $arguments);
519
+ }
520
+
521
+ /* @var $dbConfig Mana_Db_Helper_Config */
522
+ $dbConfig = Mage::helper('mana_db/config');
523
+ if (!($scopeXml = $dbConfig->getScopeXml($entityName))) {
524
+ return Mage::getModel('mana_db/entity', $arguments);
525
+ }
526
+
527
+ if (!empty($scopeXml->flattens)) {
528
+ $entityName = (string)$scopeXml->flattens;
529
+ $resolvedEntityName = $this->getScopedName($entityName);
530
+ if ($this->modelExists($resolvedEntityName)) {
531
+ return Mage::getModel($resolvedEntityName, $arguments);
532
+ }
533
+ $scopeXml = $dbConfig->getScopeXml($entityName);
534
+ }
535
+ if (!empty($scopeXml->store_specifics_for)) {
536
+ $entityName = (string)$scopeXml->store_specifics_for;
537
+ $resolvedEntityName = $this->getScopedName($entityName);
538
+ if ($this->modelExists($resolvedEntityName)) {
539
+ return Mage::getModel($resolvedEntityName, $arguments);
540
+ }
541
+ }
542
+ return Mage::getModel('mana_db/entity', $arguments);
543
+ }
544
+
545
+ public function resourceExists($entityName) {
546
+ if ($className = Mage::getConfig()->getResourceModelClassName($entityName)) {
547
+ return $this->classExists($className);
548
+ }
549
+ else {
550
+ return false;
551
+ }
552
+
553
+ }
554
+
555
+ public function modelExists($entityName) {
556
+ if ($className = Mage::getConfig()->getModelClassName($entityName)) {
557
+ return $this->classExists($className);
558
+ }
559
+ else {
560
+ return false;
561
+ }
562
+
563
+ }
564
+
565
+ public function classExists($class) {
566
+ if (defined('COMPILER_INCLUDE_PATH')) {
567
+ $classFile = $class;
568
+ }
569
+ else {
570
+ $classFile = str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $class)));
571
+ }
572
+ $classFile .= '.php';
573
+ foreach (explode(PS, get_include_path()) as $path) {
574
+ if (file_exists($path.DS.$classFile)) {
575
+ return true;
576
+ }
577
+ }
578
+
579
+ return false;
580
+ }
581
  }
app/code/local/Mana/Db/Helper/Formula.php ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula extends Mage_Core_Helper_Abstract {
13
+ protected $_orders;
14
+ static $_types = array(
15
+ 'varchar' => 'string',
16
+ 'mediumtext' => 'string',
17
+ 'text' => 'string',
18
+ 'tinyint' => 'int',
19
+ 'smallint' => 'int',
20
+ 'int' => 'int',
21
+ 'bigint' => 'int',
22
+ );
23
+ static $_numericTypes = array(
24
+ 8 => 'tinyint',
25
+ 16 => 'smallint',
26
+ 32 => 'int',
27
+ );
28
+
29
+ protected $_typeCache = array();
30
+
31
+ /**
32
+ * @param string $entity
33
+ * @param string[] $formulas
34
+ * @param array $options
35
+ * @throws Exception|Mana_Db_Exception_Formula
36
+ * @return Mana_Db_Model_Formula_Context
37
+ */
38
+ public function select($entity, $formulas, $options = array()) {
39
+ $options = array_merge(array(
40
+ 'process_all_fields' => true,
41
+ ), $options);
42
+
43
+ /* @var $select Varien_Db_Select */
44
+ /* @var $selector Mana_Db_Helper_Formula_Selector */
45
+ /* @var $context Mana_Db_Model_Formula_Context */
46
+ $this->_initSelect($entity, $options, $select, $context, $selector);
47
+
48
+ // process formulas
49
+ foreach ($this->_getFieldFormulas($context, $formulas, $options) as $field) {
50
+ $context->setField($field);
51
+ try {
52
+ if ($field->getRole()) {
53
+ if ($field->hasFormula()) {
54
+ $selector->selectFormula($context, $field->getFormula());
55
+ $context->addField($field->getName());
56
+ }
57
+ else {
58
+ if ($selector->selectSystemField($context)) {
59
+ $context->addField($field->getName());
60
+ }
61
+ }
62
+ }
63
+ elseif ($field->hasFormula()) {
64
+ $selector->selectFormula($context, $field->getFormula());
65
+ $context->addField($field->getName());
66
+ }
67
+ elseif ($field->hasValue()) {
68
+ $selector->selectValue($context, $field->getValue());
69
+ $context->addField($field->getName());
70
+ }
71
+ else {
72
+ $selector->selectDefaultValue($context);
73
+ $context->addField($field->getName());
74
+ }
75
+ }
76
+ catch (Mana_Db_Exception_Formula $e) {
77
+ if ($context->getOption('provide_field_details_in_exceptions')) {
78
+ if ($field->hasFormula()) {
79
+ $e->addMessage($this->__(" in entity: '%s', field: '%s', formula '%s'", $entity, $field->getName(), $field->getFormulaString()));
80
+ }
81
+ else {
82
+ $e->addMessage($this->__(" in entity: '%s', field: '%s'", $entity, $field->getName()));
83
+ }
84
+ }
85
+ throw $e;
86
+ }
87
+ }
88
+
89
+ return $context;
90
+ }
91
+
92
+ /**
93
+ * @param Mana_Db_Model_Entity $model
94
+ * @return mixed
95
+ */
96
+ public function evaluate($model) {
97
+ /* @var $evaluator Mana_Db_Helper_Formula_Evaluator */
98
+ $evaluator = Mage::helper('mana_db/formula_evaluator');
99
+
100
+ /* @var $context Mana_Db_Model_Formula_Context */
101
+ $context = Mage::getModel('mana_db/formula_context');
102
+ $context
103
+ ->setEntity($model->getScope())
104
+ ->setModel($model)
105
+ ->setProcessor('entity')
106
+ ->setHelper($evaluator);
107
+
108
+ if ($defaultFormulas = $model->getDefaultFormulas()) {
109
+ $defaultFormulas = json_decode($defaultFormulas, true);
110
+ }
111
+ else {
112
+ $defaultFormulas = array();
113
+ }
114
+
115
+ foreach ($this->_getFieldFormulas($context, $defaultFormulas) as $field) {
116
+ if ($field->useDefault($model)) {
117
+ if ($field->hasFormula()) {
118
+ $evaluator->evaluateFormula($context, $field);
119
+ }
120
+ elseif ($field->hasValue()) {
121
+ $evaluator->evaluateValue($context, $field);
122
+ }
123
+ }
124
+ }
125
+
126
+ return $context->getResult();
127
+ }
128
+
129
+ /**
130
+ * @param $formula
131
+ * @return Mana_Db_Model_Formula_Node
132
+ */
133
+ public function parse($formula) {
134
+ /* @var $parser Mana_Db_Helper_Formula_Parser */
135
+ $parser = Mage::helper('mana_db/formula_parser');
136
+
137
+ return $parser->parse($formula);
138
+ }
139
+
140
+ /**
141
+ * @param Mana_Db_Model_Formula_Context $context
142
+ * @param Mana_Db_Model_Formula_Node $formula
143
+ * @return string[]
144
+ */
145
+ public function depends($context, $formula) {
146
+ /* @var $dependencyFinder Mana_Db_Helper_Formula_DependencyFinder */
147
+ $dependencyFinder = Mage::helper('mana_db/formula_dependencyFinder');
148
+ return $dependencyFinder->findInFormula($context, $formula);
149
+ }
150
+
151
+
152
+ public function getFormulaGroups($entity, $options = array()) {
153
+ /* @var $select Varien_Db_Select */
154
+ /* @var $selector Mana_Db_Helper_Formula_Selector */
155
+ /* @var $context Mana_Db_Model_Formula_Context */
156
+ $this->_initSelect($entity, $options, $select, $context, $selector);
157
+ $idExpr = "`{$context->registerAlias('primary')}`.`id`";
158
+ $hashExpr = "`{$context->registerAlias('primary')}`.`default_formula_hash`";
159
+ $formulasExpr = "`{$context->registerAlias('primary')}`.`default_formulas`";
160
+
161
+
162
+ $select
163
+ ->distinct()
164
+ ->columns(array('id' => new Zend_Db_Expr("MIN($idExpr)")))
165
+ ->columns(array('hash' => new Zend_Db_Expr("$hashExpr")))
166
+ ->group('hash');
167
+
168
+ /* @var $resource Mana_Db_Resource_Formula */
169
+ $resource = Mage::getResourceSingleton('mana_db/formula');
170
+ $db = $resource->getReadConnection();
171
+ $hashes = $db->fetchPairs($select);
172
+ if (($nullId = array_search(null, $hashes)) !== false) {
173
+ unset($hashes[$nullId]);
174
+ }
175
+
176
+ if (count($hashes)) {
177
+ $this->_initSelect($entity, $options, $select, $context, $selector);
178
+ $select
179
+ ->columns(array('id' => new Zend_Db_Expr("$idExpr")))
180
+ ->columns(array('formulas' => new Zend_Db_Expr("$formulasExpr")))
181
+ ->where("$idExpr IN (?)", array_keys($hashes));
182
+
183
+ $formulas = $db->fetchPairs($select);
184
+
185
+ $result = array_combine($hashes, $formulas);
186
+ }
187
+ else {
188
+ $result = array();
189
+ }
190
+ if ($nullId !== false) {
191
+ $result[''] = '';
192
+ }
193
+ return $result;
194
+ }
195
+
196
+ /**
197
+ * @param string $entity
198
+ * @param string $targetEntity
199
+ * @param array $options
200
+ * @throws Exception
201
+ * @return string
202
+ */
203
+ public function delete($entity, $targetEntity, $options = array()) {
204
+ throw new Exception('Not implemented');
205
+ /* @var $select Varien_Db_Select */
206
+ /* @var $selector Mana_Db_Helper_Formula_Selector */
207
+ /* @var $context Mana_Db_Model_Formula_Context */
208
+ $this->_initSelect($entity, $options, $select, $context, $selector);
209
+
210
+ /* @var $resource Mana_Db_Resource_Formula */
211
+ $resource = Mage::getResourceSingleton('mana_db/formula');
212
+
213
+ /* @var $dbHelper Mana_Db_Helper_Data */
214
+ $dbHelper = Mage::helper('mana_db');
215
+
216
+ $select->setPart(Varien_Db_Select::FROM, array());
217
+ return $select->deleteFromSelect($resource->getTable($dbHelper->getScopedName($targetEntity)));
218
+ }
219
+
220
+ /**
221
+ * @param string $entity
222
+ * @param array $options
223
+ * @param Varien_Db_Select $select
224
+ * @param Mana_Db_Model_Formula_Context $context
225
+ * @param Mana_Db_Helper_Formula_Selector $selector
226
+ */
227
+ protected function _initSelect($entity, $options, &$select, &$context, &$selector) {
228
+ /* @var $dbConfig Mana_Db_Helper_Config */
229
+ $dbConfig = Mage::helper('mana_db/config');
230
+
231
+ $scopeXml = $dbConfig->getScopeXml($entity);
232
+
233
+ /* @var $selector Mana_Db_Helper_Formula_Selector */
234
+ $selector = Mage::helper('mana_db/formula_selector');
235
+
236
+ /* @var $normalEntity Mana_Db_Helper_Formula_Entity_Normal */
237
+ $normalEntity = Mage::helper('mana_db/formula_entity_normal');
238
+
239
+ /* @var $context Mana_Db_Model_Formula_Context */
240
+ $context = Mage::getModel('mana_db/formula_context');
241
+
242
+ /** @noinspection PhpUndefinedFieldInspection */
243
+ $context
244
+ ->setAlias($this->createAlias(''))
245
+ ->setEntity($entity)
246
+ ->setPrimaryEntity((string)$scopeXml->flattens)
247
+ ->setTargetEntity($entity)
248
+ ->setProcessor('entity')
249
+ ->setEntityHelper($normalEntity)
250
+ ->setHelper($selector)
251
+ ->setOptions($options);
252
+
253
+ /** @noinspection PhpUndefinedFieldInspection */
254
+ $select = $this->createSelect($context, $scopeXml->formula->base);
255
+ }
256
+
257
+ /**
258
+ * @param Mana_Db_Model_Formula_Context $context
259
+ * @param string[] $formulas
260
+ * @return Mana_Db_Model_Formula_Field[]
261
+ */
262
+ protected function _getFieldFormulas($context, $formulas, $options = array()) {
263
+ $result = array();
264
+ $entity = $context->getTargetEntity();
265
+
266
+ /* @var $dbConfig Mana_Db_Helper_Config */
267
+ $dbConfig = Mage::helper('mana_db/config');
268
+
269
+ /* @var $fieldsXml Varien_Simplexml_Element */
270
+ /** @noinspection PhpUndefinedFieldInspection */
271
+ $fieldsXml = $dbConfig->getScopeXml($entity)->fields;
272
+ foreach ($fieldsXml->children() as $name => $fieldXml) {
273
+ if (empty($options['process_all_fields']) && !isset($formulas[$name])) {
274
+ continue;
275
+ }
276
+ /* @var $field Mana_Db_Model_Formula_Field */
277
+ $field = Mage::getModel('mana_db/formula_field');
278
+ $field
279
+ ->setName($name)
280
+ ->setRole(isset($fieldXml->role) ? (string)$fieldXml->role : '')
281
+ ->setType((string)$fieldXml->type);
282
+
283
+ if (isset($fieldXml->no)) {
284
+ $field->setNo((string)$fieldXml->no);
285
+ }
286
+ if (isset($formulas[$name])) {
287
+ $field
288
+ ->setFormulaString($formulas[$name])
289
+ ->setFormula($this->parse($formulas[$name]))
290
+ ->setDependencies($this->depends($context, $field->getFormula()));
291
+ }
292
+ elseif(isset($fieldXml->default_formula)) {
293
+ $field
294
+ ->setFormulaString((string)$fieldXml->default_formula)
295
+ ->setFormula($this->parse((string)$fieldXml->default_formula))
296
+ ->setDependencies($this->depends($context, $field->getFormula()));
297
+ }
298
+ elseif (isset($fieldXml->default_value)) {
299
+ $field->setValue((string)$fieldXml->default_value);
300
+ }
301
+ $result[$name] = $field;
302
+ }
303
+
304
+ $this->_sortByDependency($result);
305
+ return $result;
306
+ }
307
+
308
+ /**
309
+ * @param Mana_Db_Model_Formula_Field[] $fields
310
+ * @throws Mana_Db_Exception_Formula
311
+ * @return Mana_Db_Helper_Formula
312
+ */
313
+ protected function _sortByDependency(&$fields) {
314
+ $count = count($fields);
315
+ $orders = array();
316
+ for ($position = 0; $position < $count; $position++) {
317
+ $found = false;
318
+ foreach ($fields as $fieldName => $field) {
319
+ if (!isset($orders[$fieldName])) {
320
+ $hasUnresolvedDependency = false;
321
+ if ($field->hasDependencies()) {
322
+ foreach ($field->getDependencies() as $dependency) {
323
+ if (!isset($fields[$dependency])) {
324
+ throw new Mana_Db_Exception_Formula(Mage::helper('mana_db')->__("Field '%s' depends on undefined field '%s'", $fieldName, $dependency));
325
+ }
326
+ if (!isset($orders[$dependency])) {
327
+ // $dependency not yet sorted so $module should wait until that happens
328
+ $hasUnresolvedDependency = true;
329
+ break;
330
+ }
331
+ }
332
+ }
333
+ if (!$hasUnresolvedDependency) {
334
+ $found = $fieldName;
335
+ break;
336
+ }
337
+ }
338
+ }
339
+ if ($found) {
340
+ $orders[$found] = count($orders);
341
+ }
342
+ else {
343
+ $circular = array();
344
+ foreach ($fields as $fieldName => $field) {
345
+ if (!isset($orders[$fieldName])) {
346
+ $circular[] = $fieldName;
347
+ }
348
+ }
349
+ throw new Mana_Db_Exception_Formula(Mage::helper('mana_db')->__('Field values circularly depends on one another: %s', implode(', ', $circular)));
350
+ }
351
+ }
352
+ $this->_orders = $orders;
353
+ uasort($fields, array($this, '_sortByDependencyCallback'));
354
+
355
+ return $this;
356
+ }
357
+
358
+ /**
359
+ * @param Mana_Db_Model_Formula_Field $a
360
+ * @param Mana_Db_Model_Formula_Field $b
361
+ * @return int
362
+ */
363
+ protected function _sortByDependencyCallback($a, $b) {
364
+ $a = $this->_orders[$a->getName()];
365
+ $b = $this->_orders[$b->getName()];
366
+ if ($a == $b) return 0;
367
+
368
+ return $a < $b ? -1 : 1;
369
+ }
370
+
371
+ public function getType($sqlType) {
372
+ if (!isset($this->_typeCache[$sqlType])) {
373
+ $type = trim(strtolower($sqlType));
374
+ $pos = false;
375
+ if (($foundPos = strpos($type, '(')) !== false) {
376
+ $pos = $foundPos;
377
+ }
378
+ if (($foundPos = strpos($type, ' ')) !== false) {
379
+ $pos = $foundPos;
380
+ }
381
+ if ($pos !== false) {
382
+ $type = substr($type, 0, $pos);
383
+ }
384
+
385
+ $this->_typeCache[$sqlType] = isset(self::$_types[$type]) ? self::$_types[$type] : '';
386
+ }
387
+ return $this->_typeCache[$sqlType];
388
+ }
389
+
390
+ public function getMinimumNumericType($value) {
391
+ foreach (self::$_numericTypes as $bit => $type) {
392
+ if (abs($value) < 1 << $bit - 1) {
393
+ return $type;
394
+ }
395
+ }
396
+ return 'bigint';
397
+ }
398
+
399
+ /**
400
+ * @param Mana_Db_Model_Formula_Context $context
401
+ * @param SimpleXMLElement $selectXml
402
+ * @return Varien_Db_Select
403
+ */
404
+ public function createSelect($context, $selectXml) {
405
+ /* @var $dbHelper Mana_Db_Helper_Data */
406
+ $dbHelper = Mage::helper('mana_db');
407
+
408
+ /* @var $resource Mana_Db_Resource_Formula */
409
+ $resource = Mage::getResourceSingleton('mana_db/formula');
410
+
411
+ $select = $context->getSelect();
412
+
413
+ /* @var $fromXml SimpleXMLElement */
414
+ /** @noinspection PhpUndefinedFieldInspection */
415
+ $fromXml = $selectXml->from;
416
+ foreach ($fromXml->children() as $alias => $definition) {
417
+ $entity = $alias == 'primary' ? $context->getPrimaryEntity() : (string)$definition->entity;
418
+ $select->from(
419
+ array($context->registerAlias($alias) => $resource->getTable($dbHelper->getScopedName($entity))),
420
+ null
421
+ );
422
+ }
423
+
424
+ if (isset($selectXml->join)) {
425
+ $joinXml = $selectXml->join;
426
+ /* @var $joinXml SimpleXMLElement */
427
+ foreach ($joinXml->children() as $alias => $definition) {
428
+ $method = isset($definition->type) ? 'join' . ucfirst($definition->type) : 'joinInner';
429
+ $entity = $alias == 'primary' ? $context->getPrimaryEntity() : (string)$definition->entity;
430
+ $select->$method(
431
+ array($context->registerAlias($alias) => $resource->getTable($dbHelper->getScopedName($entity))),
432
+ $context->resolveAliases((string)$definition->on),
433
+ null
434
+ );
435
+ }
436
+ }
437
+
438
+ if (isset($selectXml->order)) {
439
+ $select->order($context->resolveAliases((string)$selectXml->order, false));
440
+
441
+ }
442
+ if (isset($selectXml->where)) {
443
+ $select->where($context->resolveAliases((string)$selectXml->where));
444
+
445
+ }
446
+
447
+ if ($formula = $context->getOption('entity_filter_formula')) {
448
+ /* @var $selector Mana_Db_Helper_Formula_Selector */
449
+ $selector = Mage::helper('mana_db/formula_selector');
450
+ $selector->filterFormula($context, $this->parse($formula), $context->getOption('entity_filter_id'));
451
+ }
452
+
453
+ return $select;
454
+ }
455
+
456
+ /**
457
+ * @param string | string[] $value
458
+ * @return Mana_Db_Model_Formula_Alias
459
+ */
460
+ public function createAlias($value) {
461
+ if (!$value || $value == 'this') {
462
+ return Mage::getModel('mana_db/formula_alias_empty');
463
+ }
464
+ elseif (is_array($value)) {
465
+ return Mage::getModel('mana_db/formula_alias_array', $value);
466
+ }
467
+ else {
468
+ return Mage::getModel('mana_db/formula_alias_single', $value);
469
+ }
470
+ }
471
+ }
app/code/local/Mana/Db/Helper/Formula/Abstract.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Abstract extends Mage_Core_Helper_Abstract {
13
+ const CLASS_PREFIX = 'Mana_Db_Model_Formula_Node_';
14
+ protected $_methodPrefix = '';
15
+
16
+ /**
17
+ * @var Mana_Db_Model_Formula_Expr
18
+ */
19
+ protected $_exprPrototype;
20
+
21
+ public function __construct() {
22
+ $this->_exprPrototype = Mage::getModel('mana_db/formula_expr');
23
+ }
24
+
25
+ /**
26
+ * @return Mana_Db_Model_Formula_Expr
27
+ */
28
+ public function expr() {
29
+ return clone $this->_exprPrototype;
30
+ }
31
+
32
+ /**
33
+ * @param Mana_Db_Model_Formula_Expr $expr
34
+ * @param string $type
35
+ * @return Mana_Db_Model_Formula_Expr
36
+ */
37
+ public function cast($expr, $type) {
38
+ return $expr;
39
+ }
40
+
41
+ public function _call($args) {
42
+ /* @var $core Mana_Core_Helper_Data */
43
+ $core = Mage::helper('mana_core');
44
+ $formula = $args[1];
45
+ $class = get_class($formula);
46
+ if ($core->startsWith($class, self::CLASS_PREFIX)) {
47
+ return call_user_func_array(array($this, $this->_methodPrefix . substr($class, strlen(self::CLASS_PREFIX))), $args);
48
+ }
49
+ else {
50
+ throw new $this->__("Formula node class must start with '%s'", self::CLASS_PREFIX);
51
+ }
52
+ }
53
+
54
+ public function __call($name, $args) {
55
+ /* @var $core Mana_Core_Helper_Data */
56
+ $core = Mage::helper('mana_core');
57
+
58
+ if ($core->startsWith($name, $this->_methodPrefix)) {
59
+ return call_user_func_array(array($this, $this->_methodPrefix . 'Node'), $args);
60
+ }
61
+ else {
62
+ throw new $this->__("Unknown method %s::%s", __CLASS__, $name);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * @param string $name
68
+ * @return Mana_Db_Helper_Formula_Function
69
+ */
70
+ public function getFunction($name) {
71
+ return Mage::helper('mana_db/formula_function_' . lcfirst(uc_words(strtolower($name), '')));
72
+ }
73
+ }
app/code/local/Mana/Db/Helper/Formula/DependencyFinder.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_DependencyFinder extends Mana_Db_Helper_Formula_Abstract {
13
+ protected $_methodPrefix = '_findIn';
14
+ /**
15
+ * @param Mana_Db_Model_Formula_Context $context
16
+ * @param Mana_Db_Model_Formula_Node $formula
17
+ * @return string[]
18
+ */
19
+ public function findInFormula(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
20
+ return $this->_call(func_get_args());
21
+ }
22
+
23
+ /**
24
+ * @param Mana_Db_Model_Formula_Context $context
25
+ * @param Mana_Db_Model_Formula_Node_Add $formula
26
+ * @return string[]
27
+ */
28
+ protected function _findInAdd($context, $formula) {
29
+ return array_merge($this->findInFormula($context, $formula->a), $this->findInFormula($context, $formula->b));
30
+ }
31
+
32
+ /**
33
+ * @param Mana_Db_Model_Formula_Context $context
34
+ * @param Mana_Db_Model_Formula_Node_Field $formula
35
+ * @return string[]
36
+ */
37
+ protected function _findInField(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
38
+ if (count($formula->identifiers) == 2 && $formula->identifiers[0] == 'this') {
39
+ return array($formula->identifiers[1]);
40
+ }
41
+ else {
42
+ return array();
43
+ }
44
+ }
45
+
46
+ /**
47
+ * @param Mana_Db_Model_Formula_Context $context
48
+ * @param Mana_Db_Model_Formula_Node_FormulaExpr $formula
49
+ * @return string[]
50
+ */
51
+ protected function _findInFormulaExpr($context, $formula) {
52
+ $result = array();
53
+ foreach ($formula->parts as $part) {
54
+ $result = array_merge($result, $this->findInFormula($context, $part));
55
+ }
56
+ return $result;
57
+ }
58
+
59
+ /**
60
+ * @param Mana_Db_Model_Formula_Context $context
61
+ * @param Mana_Db_Model_Formula_Node_FunctionCall $formula
62
+ * @return string[]
63
+ */
64
+ protected function _findInFunctionCall($context, $formula) {
65
+ $result = array();
66
+ foreach ($formula->args as $arg) {
67
+ $result = array_merge($result, $this->findInFormula($context, $arg));
68
+ }
69
+
70
+ return $result;
71
+ }
72
+
73
+ /**
74
+ * @param Mana_Db_Model_Formula_Context $context
75
+ * @param Mana_Db_Model_Formula_Node_Identifier $formula
76
+ * @return string[]
77
+ */
78
+ protected function _findInIdentifier(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
79
+ return array($formula->identifier);
80
+ }
81
+
82
+ /**
83
+ * @param Mana_Db_Model_Formula_Context $context
84
+ * @param Mana_Db_Model_Formula_Node_Multiply $formula
85
+ * @return string[]
86
+ */
87
+ protected function _findInMultiply($context, $formula) {
88
+ return array_merge($this->findInFormula($context, $formula->a), $this->findInFormula($context, $formula->b));
89
+ }
90
+
91
+ /**
92
+ * @param Mana_Db_Model_Formula_Context $context
93
+ * @param Mana_Db_Model_Formula_Node $formula
94
+ * @return string[]
95
+ */
96
+ protected function _findInNode(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
97
+ return array();
98
+ }
99
+
100
+ }
app/code/local/Mana/Db/Helper/Formula/Entity.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ abstract class Mana_Db_Helper_Formula_Entity extends Mage_Core_Helper_Abstract {
13
+ protected $_name;
14
+
15
+ /**
16
+ *
17
+ * @param Mana_Db_Model_Formula_Context $context
18
+ * @param Mana_Db_Model_Formula_Entity $entity
19
+ */
20
+ public abstract function select($context, $entity);
21
+
22
+ /**
23
+ * @param Mana_Db_Model_Formula_Context $context
24
+ * @param Mana_Db_Model_Formula_Entity $entity
25
+ */
26
+ public function endSelect($context, $entity) {
27
+ }
28
+
29
+ /**
30
+ * @param Mana_Db_Model_Formula_Context $context
31
+ * @param Mana_Db_Model_Formula_Node_Field $formula
32
+ * @param Mana_Db_Model_Formula_Expr $expr
33
+ */
34
+ public abstract function selectField($context, $formula, $expr);
35
+
36
+ public function getName() {
37
+ if (!$this->_name) {
38
+ $class = substr(get_class($this), strlen(__CLASS__ . '_'));
39
+ $this->_name = strtolower($class);
40
+ }
41
+
42
+ return $this->_name;
43
+ }
44
+
45
+ }
app/code/local/Mana/Db/Helper/Formula/Entity/Aggregate.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Entity_Aggregate extends Mana_Db_Helper_Formula_Entity {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Entity $entity
16
+ */
17
+ public function select($context, $entity) {
18
+ switch ($context->getMode()) {
19
+ default:
20
+ $context
21
+ ->setMode($this->getName())
22
+ ->setEntityHelper($this);
23
+
24
+ /* @var $dbHelper Mana_Db_Helper_Data */
25
+ $dbHelper = Mage::helper('mana_db');
26
+
27
+ /* @var $resource Mana_Db_Resource_Formula */
28
+ $resource = Mage::getResourceSingleton('mana_db/formula');
29
+
30
+ $context->incrementPrefix();
31
+
32
+ $alias = explode('.', $entity->getAlias());
33
+ $alias = array_pop($alias);
34
+
35
+ $aggregateContext = $context->createChildContext()
36
+ ->setPrefix($context->getPrefix())
37
+ ->setEntity($entity->getEntity())
38
+ ->setProcessor($entity->getProcessor())
39
+ ->setAlias($alias);
40
+
41
+ $select = $aggregateContext->getSelect()
42
+ ->from(array(
43
+ $aggregateContext->registerAlias($alias)
44
+ => $resource->getTable($dbHelper->getScopedName($entity->getEntity()))
45
+ ), null);
46
+
47
+ if ($entity->getJoin()) {
48
+ foreach ($entity->getJoin() as $alias => $join) {
49
+ $method = isset($join['type']) ? 'join' . ucfirst($join['type']) : 'joinInner';
50
+ $select->$method(
51
+ array(
52
+ $aggregateContext->registerAlias($alias) =>
53
+ $resource->getTable($dbHelper->getScopedName($join['entity']))
54
+ ),
55
+ $aggregateContext->resolveAliases($join['on']),
56
+ null
57
+ );
58
+ }
59
+ }
60
+ if ($entity->getOrder()) {
61
+ $select->order($aggregateContext->resolveAliases($entity->getOrder(), false));
62
+ }
63
+ if ($entity->getWhere()) {
64
+ $select->where($aggregateContext->resolveAliases($entity->getWhere()));
65
+ }
66
+
67
+ $context
68
+ ->setEntity($entity->getEntity())
69
+ ->setProcessor($entity->getProcessor())
70
+ ->setAlias($entity->getAlias())
71
+ ->setAggregateContext($aggregateContext);
72
+ break;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * @param Mana_Db_Model_Formula_Context $context
78
+ * @param Mana_Db_Model_Formula_Node_Field $formula
79
+ * @param Mana_Db_Model_Formula_Expr $expr
80
+ */
81
+ public function selectField($context, $formula, $expr) {
82
+ $expr
83
+ ->setIsAggregate(true)
84
+ ->setSubSelect($context->getAggregateContext()->getSelect());
85
+ }
86
+
87
+ }
app/code/local/Mana/Db/Helper/Formula/Entity/Aggregate/Field.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Entity_Aggregate_Field extends Mana_Db_Helper_Formula_Entity {
13
+ /**
14
+ *
15
+ * @param Mana_Db_Model_Formula_Context $context
16
+ * @param Mana_Db_Model_Formula_Entity $entity
17
+ */
18
+ public function select($context, $entity) {
19
+ switch ($context->getMode()) {
20
+ default:
21
+ $context
22
+ ->setMode($this->getName())
23
+ ->setEntityHelper($this);
24
+
25
+ /* @var $resource Mana_Db_Resource_Formula */
26
+ $resource = Mage::getResourceSingleton('mana_db/formula');
27
+
28
+ /* @var $dbHelper Mana_Db_Helper_Data */
29
+ $dbHelper = Mage::helper('mana_db');
30
+
31
+ foreach ($entity->getAggregateFields() as $field) {
32
+ if (!$context->hasAlias($field['alias'])) {
33
+ $context->getSelect()->joinLeft(
34
+ array(
35
+ $context->registerAlias($field['alias']) =>
36
+ $resource->getTable($dbHelper->getScopedName($field['entity']))
37
+ ),
38
+ $context->resolveAliases($field['join']),
39
+ null
40
+ );
41
+ }
42
+ }
43
+
44
+ $context
45
+ ->setEntity($entity->getEntity())
46
+ ->setProcessor($entity->getProcessor())
47
+ ->setAlias($entity->getAlias());
48
+ break;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * @param Mana_Db_Model_Formula_Context $context
54
+ * @param Mana_Db_Model_Formula_Node_Field $formula
55
+ * @param Mana_Db_Model_Formula_Expr $expr
56
+ */
57
+ public function selectField($context, $formula, $expr) {
58
+ $expr->setIsAggregate(true);
59
+ }
60
+ }
app/code/local/Mana/Db/Helper/Formula/Entity/Foreign.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Entity_Foreign extends Mana_Db_Helper_Formula_Entity {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Entity $entity
16
+ */
17
+ public function select($context, $entity) {
18
+ switch ($context->getMode()) {
19
+ default:
20
+ if (!$context->hasAlias($entity->getAlias()->asString(0))) {
21
+ /* @var $resource Mana_Db_Resource_Formula */
22
+ $resource = Mage::getResourceSingleton('mana_db/formula');
23
+
24
+ /* @var $dbHelper Mana_Db_Helper_Data */
25
+ $dbHelper = Mage::helper('mana_db');
26
+
27
+ /* @var $joinClosure Mana_Db_Model_Formula_Closure_ForeignJoinEnd */
28
+ $joinClosure = Mage::getModel('mana_db/formula_closure_foreignJoinEnd', compact('context', 'entity'));
29
+ $context->getAlias()->each($joinClosure);
30
+ }
31
+
32
+ $context
33
+ ->setEntity($entity->getEntity())
34
+ ->setProcessor($entity->getProcessor())
35
+ ->setAlias($entity->getAlias())
36
+ ->setEntityHelper($this);
37
+ break;
38
+ }
39
+
40
+ }
41
+
42
+ /**
43
+ * @param Mana_Db_Model_Formula_Context $context
44
+ * @param Mana_Db_Model_Formula_Node_Field $formula
45
+ * @param Mana_Db_Model_Formula_Expr $expr
46
+ */
47
+ public function selectField($context, $formula, $expr) {
48
+ $expr->setExpr($expr->getFieldExpr());
49
+ }
50
+ }
app/code/local/Mana/Db/Helper/Formula/Entity/Frontend.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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Entity_Frontend extends Mana_Db_Helper_Formula_Entity {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Entity $entity
16
+ */
17
+ public function select($context, $entity) {
18
+ switch ($context->getMode()) {
19
+ default:
20
+ $context
21
+ ->setMode($this->getName())
22
+ ->setEntityHelper($this);
23
+
24
+ $context
25
+ ->setEntity($entity->getEntity())
26
+ ->setProcessor($entity->getProcessor())
27
+ ->setAlias($entity->getAlias());
28
+ break;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @param Mana_Db_Model_Formula_Context $context
34
+ * @param Mana_Db_Model_Formula_Node_Field $formula
35
+ * @param Mana_Db_Model_Formula_Expr $expr
36
+ */
37
+ public function selectField($context, $formula, $expr) {
38
+ $expr
39
+ ->setIsFrontend(true)
40
+ ->setExpr("'{{= {$formula->__toString()} }}'")
41
+ ->setType('varchar(255');
42
+ }
43
+ }
app/code/local/Mana/Db/Helper/Formula/Entity/Normal.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Entity_Normal extends Mana_Db_Helper_Formula_Entity {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Node_Field $formula
16
+ * @param Mana_Db_Model_Formula_Expr $expr
17
+ */
18
+ public function selectField(/** @noinspection PhpUnusedParameterInspection */$context, $formula, $expr) {
19
+ foreach($context->getSelect()->getPart(Varien_Db_Select::COLUMNS) as $column) {
20
+ if ($column[2] == $expr->getFieldName()) {
21
+ $expr->setExpr($column[1]);
22
+ return;
23
+ }
24
+ }
25
+
26
+ throw new Mana_Db_Exception_Formula($this->__("Field '%s' referenced in field '%s' is not defined.", $expr->getFieldName(), $context->getField()->getName()));
27
+ }
28
+
29
+ /**
30
+ *
31
+ * @param Mana_Db_Model_Formula_Context $context
32
+ * @param Mana_Db_Model_Formula_Entity $entity
33
+ */
34
+ public function select($context, $entity) {
35
+ }
36
+ }
app/code/local/Mana/Db/Helper/Formula/Evaluator.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Evaluator extends Mana_Db_Helper_Formula_Abstract {
13
+ public function evaluateFormula($context, $field) {
14
+ throw new Exception('Not implemented');
15
+ }
16
+
17
+ public function evaluateValue($context, $field) {
18
+ throw new Exception('Not implemented');
19
+ }
20
+ }
app/code/local/Mana/Db/Helper/Formula/Function.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ abstract class Mana_Db_Helper_Formula_Function extends Mage_Core_Helper_Abstract {
13
+ protected $_name;
14
+ /**
15
+ * @param Mana_Db_Model_Formula_Context $context
16
+ * @param Mana_Db_Model_Formula_Expr[] $args
17
+ * @return Mana_Db_Model_Formula_Expr
18
+ */
19
+ abstract public function select($context, $args);
20
+
21
+ public function getName() {
22
+ if (!$this->_name) {
23
+ $class = substr(get_class($this), strlen(__CLASS__ . '_'));
24
+ $this->_name = strtoupper(preg_replace('/(.)([A-Z])/', "$1_$2", $class));
25
+ }
26
+ return $this->_name;
27
+ }
28
+ }
app/code/local/Mana/Db/Helper/Formula/Function/Count.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Function_Count extends Mana_Db_Helper_Formula_Function {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Expr[] $args
16
+ * @throws Mana_Db_Exception_Formula
17
+ * @return Mana_Db_Model_Formula_Expr
18
+ */
19
+ public function select($context, $args) {
20
+ if (count($args) != 1) {
21
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects one parameter", $this->getName()));
22
+ }
23
+ if (!$args[0]->getIsAggregate()) {
24
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects 1 parameter to be a field of aggregate entity", $this->getName()));
25
+ }
26
+
27
+ $helper = $context->getHelper();
28
+
29
+ if ($args[0]->getSubSelect()) {
30
+ $expr = "COUNT({$args[0]->getFieldExpr()})";
31
+
32
+ return $helper->expr()->setExpr("({$args[0]->getSubSelect()->columns($expr)})")->setType('int');
33
+ }
34
+ else {
35
+ $expr = '';
36
+ $fieldExpr = $args[0]->getFieldExpr();
37
+ foreach ($fieldExpr as $field) {
38
+ if ($expr) {
39
+ $expr .= " + ";
40
+ }
41
+ $expr .= "IF ($field IS NULL, 0, 1)";
42
+ }
43
+
44
+ return $helper->expr()->setExpr($expr)->setType('int');
45
+ }
46
+ }
47
+ }
app/code/local/Mana/Db/Helper/Formula/Function/Glue.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Function_Glue extends Mana_Db_Helper_Formula_Function {
13
+ protected $_separator = '{~#';
14
+ /**
15
+ * @param Mana_Db_Model_Formula_Context $context
16
+ * @param Mana_Db_Model_Formula_Expr[] $args
17
+ * @throws Mana_Db_Exception_Formula
18
+ * @return Mana_Db_Model_Formula_Expr
19
+ */
20
+ public function select($context, $args) {
21
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
22
+ $formulaHelper = Mage::helper('mana_db/formula');
23
+
24
+ if (count($args) < 2 || count($args) > 3) {
25
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects 2 or 3 parameters", $this->getName()));
26
+ }
27
+ if (!$args[0]->getIsAggregate()) {
28
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects 1 parameter to be a field of aggregate entity", $this->getName()));
29
+ }
30
+
31
+ $helper = $context->getHelper();
32
+
33
+ $sep = $helper->cast($args[1], 'varchar (255)')->getExpr();
34
+ $lastSep = count($args) == 3 ? $helper->cast($args[2], 'varchar (255)')->getExpr() : '';
35
+ if ($args[0]->getSubSelect()) {
36
+ $field = $helper->cast($args[0], 'varchar (255)')->getFieldExpr();
37
+ if (count($args) == 3) {
38
+ $expr =
39
+ "IF (COUNT({$args[0]->getFieldExpr()}) > 1, ".
40
+ "REPLACE(CONCAT(".
41
+ "SUBSTRING_INDEX(GROUP_CONCAT({$field} SEPARATOR '{$this->_separator}'), ".
42
+ "'{$this->_separator}', COUNT({$field}) - 1), ".
43
+ "$lastSep, ".
44
+ "SUBSTRING_INDEX(GROUP_CONCAT({$field} SEPARATOR '{$this->_separator}'), ".
45
+ "'{$this->_separator}', -1)".
46
+ "), '{$this->_separator}', $sep), ".
47
+ "GROUP_CONCAT({$field} SEPARATOR $sep)".
48
+ ")";
49
+ }
50
+ else {
51
+ $expr = "GROUP_CONCAT({$field} SEPARATOR $sep)";
52
+ }
53
+
54
+ return $helper->expr()->setExpr("({$args[0]->getSubSelect()->columns($expr)})")->setType('varchar(255)');
55
+ }
56
+ else {
57
+ $expr = '';
58
+ $fieldExpr = $args[0]->getFieldExpr();
59
+ foreach ($fieldExpr as $index => $field) {
60
+ $nextField = isset($fieldExpr[$index + 1]) ? $fieldExpr[$index + 1] : '';
61
+ if ($expr) {
62
+ if (count($args) == 3) {
63
+ if ($nextField) {
64
+ $expr .= ", IF ($field IS NULL, '', CONCAT(IF ($nextField IS NULL, $lastSep, $sep), $field))";
65
+ }
66
+ else {
67
+ $expr .= ", IF ($field IS NULL, '', CONCAT($lastSep, $field))";
68
+ }
69
+ }
70
+ else {
71
+ $expr .= ", IF ($field IS NULL, '', CONCAT($sep, $field))";
72
+ }
73
+ }
74
+ else {
75
+ $expr .= "COALESCE({$helper->cast($args[0]->setExpr($field), 'varchar (255)')->getExpr()}, '')";
76
+ }
77
+ }
78
+ $expr = "CONCAT($expr)";
79
+
80
+ return $helper->expr()->setExpr($expr)->setType('varchar(255)');
81
+ }
82
+ }
83
+ }
app/code/local/Mana/Db/Helper/Formula/Function/If.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Function_If extends Mana_Db_Helper_Formula_Function {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Expr[] $args
16
+ * @throws Mana_Db_Exception_Formula
17
+ * @throws Exception
18
+ * @return Mana_Db_Model_Formula_Expr
19
+ */
20
+ public function select($context, $args) {
21
+ if (count($args) != 3) {
22
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects three parameters", $this->getName()));
23
+ }
24
+ if ($args[0]->getIsAggregate()) {
25
+ throw new Mana_Db_Exception_Formula($this->__("You can only use aggregate function on aggregate fields"));
26
+ }
27
+
28
+ /* @var $helper Mana_Db_Helper_Formula_Selector */
29
+ $helper = $context->getHelper();
30
+
31
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
32
+ $formulaHelper = Mage::helper('mana_db/formula');
33
+
34
+ $condition = $args[0]->getExpr();
35
+ switch ($formulaHelper->getType($args[0]->getType())) {
36
+ case 'string':
37
+ $condition .= "<> ''";
38
+ break;
39
+ case 'int':
40
+ $condition .= "<> 0";
41
+ break;
42
+ case 'bool':
43
+ break;
44
+ default:
45
+ throw new Exception('Not implemented');
46
+ }
47
+
48
+ $helper->binaryCast($args[1], $args[2]);
49
+ return $helper->expr()->setExpr("IF({$condition}, {$args[1]->getExpr()}, {$args[2]->getExpr()})")->setType($args[1]->getType());
50
+ }
51
+ }
app/code/local/Mana/Db/Helper/Formula/Function/Lower.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Function_Lower extends Mana_Db_Helper_Formula_Function {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Expr[] $args
16
+ * @throws Mana_Db_Exception_Formula
17
+ * @return Mana_Db_Model_Formula_Expr
18
+ */
19
+ public function select($context, $args) {
20
+ if (count($args) != 1) {
21
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects one parameter", $this->getName()));
22
+ }
23
+ if ($args[0]->getIsAggregate()) {
24
+ throw new Mana_Db_Exception_Formula($this->__("You can only use aggregate function on aggregate fields"));
25
+ }
26
+
27
+ $helper = $context->getHelper();
28
+ return $helper->expr()->setExpr("LOWER({$helper->cast($args[0], 'varchar(255)')->getExpr()})")->setType('varchar(255)');
29
+ }
30
+ }
app/code/local/Mana/Db/Helper/Formula/Function/Seo.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Function_Seo extends Mana_Db_Helper_Formula_Function {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Expr[] $args
16
+ * @throws Mana_Db_Exception_Formula
17
+ * @return Mana_Db_Model_Formula_Expr
18
+ */
19
+ public function select($context, $args) {
20
+ if (count($args) != 1) {
21
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects one parameter", $this->getName()));
22
+ }
23
+ if ($args[0]->getIsAggregate()) {
24
+ throw new Mana_Db_Exception_Formula($this->__("You can only use aggregate function on aggregate fields"));
25
+ }
26
+
27
+ $helper = $context->getHelper();
28
+ /* @var $seo Mana_Core_Helper_Seo */
29
+ $seo = Mage::helper('mana_core/seo');
30
+
31
+ return $helper->expr()->setExpr("{$seo->select("LOWER({$helper->cast($args[0], 'varchar(255)')->getExpr()})")}")->setType('varchar(255)');
32
+ }
33
+ }
app/code/local/Mana/Db/Helper/Formula/Function/SkinUrl.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Function_SkinUrl extends Mana_Db_Helper_Formula_Function {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param Mana_Db_Model_Formula_Expr[] $args
16
+ * @throws Mana_Db_Exception_Formula
17
+ * @return Mana_Db_Model_Formula_Expr
18
+ */
19
+ public function select($context, $args) {
20
+ if (count($args) != 1) {
21
+ throw new Mana_Db_Exception_Formula($this->__("Function '%s' expects one parameter", $this->getName()));
22
+ }
23
+ if ($args[0]->getIsAggregate()) {
24
+ throw new Mana_Db_Exception_Formula($this->__("You can only use aggregate function on aggregate fields"));
25
+ }
26
+
27
+ $helper = $context->getHelper();
28
+
29
+ $urlConstant = str_replace('\'', '\\\'', substr($args[0]->getExpr(), 1, strlen($args[0]->getExpr())));
30
+ return $helper->expr()->setExpr("'{{= {$this->getName()}(\\'$urlConstant\\')'")->setType('varchar(255)');
31
+ }
32
+ }
app/code/local/Mana/Db/Helper/Formula/Parser.php ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ * Language symbols:
12
+ *
13
+ * STRING ::= { ANY - '{{=' }
14
+ * Language syntax:
15
+ *
16
+ * Text ::= { Formula | String } // no spaces
17
+ * String ::= { ANY } until '{{=' | EOF )
18
+ * Formula ::= '{{=' Expr '}}' // spaces separate tokens
19
+ * Expr ::= AddExpr
20
+ * AddExpr ::= MulExpr ('+' | '-') Expr
21
+ * MulExpr ::= PrimaryExpr ('*' | '/') Expr
22
+ * PrimaryExpr ::= Function | Field | String | Number | '(' Expr ')'
23
+ * Function ::= identifier '(' { Expr ',' } ')'
24
+ * Field ::= [ identifier '.' ] identifier
25
+ */
26
+ class Mana_Db_Helper_Formula_Parser extends Mage_Core_Helper_Abstract {
27
+ const OPEN_PAR = 1;
28
+ const CLOSE_PAR = 2;
29
+ const OPEN_FORMULA = 3;
30
+ const CLOSE_FORMULA = 4;
31
+ const IDENTIFIER = 5;
32
+ const DOT = 6;
33
+ const NUMBER = 7;
34
+ const STRING = 8;
35
+ const COMMA = 9;
36
+ const MULTIPLY = 10;
37
+ const DIVIDE = 11;
38
+ const ADD = 12;
39
+ const SUBTRACT = 13;
40
+ const EOF = 14;
41
+
42
+ static $_tokens = array(
43
+ self::OPEN_PAR => '(',
44
+ self::CLOSE_PAR => ')',
45
+ self::OPEN_FORMULA => '{{=',
46
+ self::CLOSE_FORMULA => '}}',
47
+ self::DOT => '.',
48
+ self::COMMA => ',',
49
+ self::MULTIPLY => '*',
50
+ self::DIVIDE => '/',
51
+ self::ADD => '+',
52
+ self::SUBTRACT => '-',
53
+ );
54
+ static $_multiplicationOperators = array(self::MULTIPLY, self::DIVIDE);
55
+ static $_additionOperators = array(self::ADD, self::SUBTRACT);
56
+
57
+ static $_numeric = '0123456789';
58
+ static $_firstIdentifierSymbol = '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
59
+ static $_nextIdentifierSymbol = '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
60
+ static $_whitespace = " \t\r\n";
61
+ protected $_formula;
62
+ protected $_pos;
63
+ protected $_token;
64
+
65
+ /**
66
+ * @param string $formula
67
+ * @return Mana_Db_Model_Formula_Node
68
+ */
69
+ public function parse($formula) {
70
+ $parts = array();
71
+ $fromPos = 0;
72
+ for ($pos = strpos($formula, self::$_tokens[self::OPEN_FORMULA]); $pos !== false;
73
+ $pos = strpos($formula, self::$_tokens[self::OPEN_FORMULA], $fromPos))
74
+ {
75
+ if ($fromPos < $pos) {
76
+ $parts[] = $this->_parseString(substr($formula, $fromPos, $pos - $fromPos));
77
+ }
78
+ if (($fromPos = strpos($formula, self::$_tokens[self::CLOSE_FORMULA], $pos + strlen(self::$_tokens[self::OPEN_FORMULA]))) !== false) {
79
+ $parts[] = $this->parseFormula(substr($formula, $pos + strlen(self::$_tokens[self::OPEN_FORMULA]), $fromPos - ($pos + 3)));
80
+ $fromPos += strlen(self::$_tokens[self::CLOSE_FORMULA]);
81
+ }
82
+ else {
83
+ $this->_error(self::CLOSE_FORMULA);
84
+ }
85
+ }
86
+ if ($fromPos < strlen($formula)) {
87
+ $parts[] = $this->_parseString(substr($formula, $fromPos));
88
+ }
89
+ if (!count($parts)) {
90
+ $parts[] = $this->_parseNull();
91
+ }
92
+
93
+ if (count($parts) > 1) {
94
+ return new Mana_Db_Model_Formula_Node_FormulaExpr(compact('parts'));
95
+ }
96
+ else {
97
+ return $parts[0];
98
+ }
99
+ }
100
+
101
+ protected function _parseString($value) {
102
+ return new Mana_Db_Model_Formula_Node_StringConstant(compact('value'));
103
+ }
104
+
105
+ public function parseFormula($formula) {
106
+ $this->_formula = $formula;
107
+ $this->_pos = 0;
108
+ $this->_scan();
109
+
110
+ return $this->_parseExpr();
111
+ }
112
+
113
+ protected function _parseNull() {
114
+ return new Mana_Db_Model_Formula_Node_NullValue();
115
+ }
116
+
117
+ protected function _parseExpr() {
118
+ return $this->_parseAddExpr();
119
+ }
120
+
121
+ protected function _parseAddExpr() {
122
+ $a = $this->_parseMulExpr();
123
+ $operator = $this->_token->kind;
124
+
125
+ if (in_array($operator, self::$_additionOperators)) {
126
+ $this->_scan();
127
+ $b = $this->_parseExpr();
128
+ return new Mana_Db_Model_Formula_Node_Add(compact('operator', 'a', 'b'));
129
+ }
130
+ else {
131
+ return $a;
132
+ }
133
+
134
+ }
135
+
136
+ protected function _parseMulExpr() {
137
+ $a = $this->_parsePrimaryExpr();
138
+ $operator = $this->_token->kind;
139
+
140
+ if (in_array($operator, self::$_multiplicationOperators)) {
141
+ $this->_scan();
142
+ $b = $this->_parseExpr();
143
+ return new Mana_Db_Model_Formula_Node_Multiply(compact('operator', 'a', 'b'));
144
+ }
145
+ else {
146
+ return $a;
147
+ }
148
+ }
149
+
150
+ protected function _parsePrimaryExpr() {
151
+ if ($this->_token->kind == self::OPEN_PAR) {
152
+ $this->_scan();
153
+ $result = $this->_parseExpr();
154
+ $this->_expect(self::CLOSE_PAR);
155
+ return $result;
156
+ }
157
+ elseif ($this->_token->kind == self::STRING) {
158
+ $result = new Mana_Db_Model_Formula_Node_StringConstant(array('value' => $this->_token->text));
159
+ $this->_scan();
160
+ return $result;
161
+ }
162
+ elseif ($this->_token->kind == self::NUMBER) {
163
+ $result = new Mana_Db_Model_Formula_Node_NumberConstant(array('value' => $this->_token->text));
164
+ $this->_scan();
165
+ return $result;
166
+ }
167
+ elseif ($this->_token->kind == self::IDENTIFIER) {
168
+ $identifier = $this->_token->text;
169
+ $this->_scan();
170
+ if ($this->_token->kind == self::DOT) {
171
+ return $this->_parseField($identifier);
172
+ }
173
+ elseif ($this->_token->kind == self::OPEN_PAR) {
174
+ return $this->_parseFunction($identifier);
175
+ }
176
+ else {
177
+ return new Mana_Db_Model_Formula_Node_Identifier(compact('identifier'));
178
+ }
179
+ }
180
+ else {
181
+ $this->_error(Mage::helper('mana_db')->__('Invalid expression'));
182
+ return false;
183
+ }
184
+ }
185
+
186
+ protected function _parseField($identifier) {
187
+ $identifiers = array($identifier);
188
+
189
+ while ($this->_token->kind == self::DOT) {
190
+ $this->_scan();
191
+ $identifiers[] = $this->_token->text;
192
+ $this->_expect(self::IDENTIFIER);
193
+ }
194
+
195
+ return new Mana_Db_Model_Formula_Node_Field(compact('identifiers'));
196
+ }
197
+
198
+ protected function _parseFunction($name) {
199
+ $args = array();
200
+
201
+ $this->_expect(self::OPEN_PAR);
202
+
203
+ if ($this->_token->kind != self::CLOSE_PAR) {
204
+ $args[] = $this->_parseExpr();
205
+ while ($this->_token->kind == self::COMMA) {
206
+ $this->_scan();
207
+ $args[] = $this->_parseExpr();
208
+ }
209
+ }
210
+
211
+ $this->_expect(self::CLOSE_PAR);
212
+
213
+ return new Mana_Db_Model_Formula_Node_FunctionCall(compact('name', 'args'));
214
+ }
215
+
216
+ protected function _expect($tokenKind) {
217
+ if ($this->_token->kind != $tokenKind) {
218
+ $this->_error($tokenKind);
219
+ }
220
+ $this->_scan();
221
+ }
222
+
223
+ /**
224
+ * @param $errNo
225
+ * @throws Mana_Db_Exception_Formula
226
+ */
227
+ protected function _error($errNo) {
228
+ if (is_string($errNo)) {
229
+ $err = $errNo;
230
+ }
231
+ else {
232
+ $t = Mage::helper('mana_db');
233
+ switch ($errNo) {
234
+ case self::IDENTIFIER: $err = $t->__("Identifier expected"); break;
235
+ case self::NUMBER: $err = $t->__("Number constant expected"); break;
236
+ case self::STRING: $err = $t->__("String constant expected"); break;
237
+ case self::EOF: $err = $t->__("End of formula expected"); break;
238
+ default:
239
+ if (isset(self::$_tokens[$errNo])) {
240
+ $err = $t->__("'%s' expected", self::$_tokens[$errNo]);
241
+ }
242
+ else {
243
+ $err = $t->__("Syntax error");
244
+ }
245
+ break;
246
+ }
247
+ }
248
+
249
+ throw new Mana_Db_Exception_Formula($err);
250
+ }
251
+
252
+ protected function _scan() {
253
+ $this->_token = $this->_peek(1);
254
+ $this->_pos = $this->_token->pos + $this->_token->length;
255
+ }
256
+
257
+ protected function _peek($count = 1) {
258
+ $token = null;
259
+ for ($pos = $this->_pos, $i = 0; $i < $count; $i++) {
260
+ $token = $this->_internalScan($pos);
261
+ $pos = $token->pos + $token->length;
262
+ }
263
+ return $token;
264
+ }
265
+
266
+ protected function _internalScan($pos = false) {
267
+ $kind = 0;
268
+ $text = '';
269
+ $formulaLength = strlen($this->_formula);
270
+ $state = 0;
271
+ $unexpectedEof = false;
272
+ $found = false;
273
+
274
+ if ($pos === false) {
275
+ $pos = $this->_pos;
276
+ }
277
+ $whitespacePos = $pos;
278
+ while ($pos < $formulaLength) {
279
+ $ch = substr($this->_formula, $whitespacePos++, 1);
280
+ if (strpos(self::$_whitespace, $ch) !== false) {
281
+ $pos++;
282
+ }
283
+ else {
284
+ break;
285
+ }
286
+ }
287
+ $newPos = $pos;
288
+
289
+ while (!$found) {
290
+ if ($newPos < $formulaLength) {
291
+ $ch = substr($this->_formula, $newPos++, 1);
292
+ }
293
+ elseif ($unexpectedEof) {
294
+ throw new Mana_Db_Exception_Formula(Mage::helper('mana_db')->__('Unexpected end of formula'));
295
+ }
296
+ else {
297
+ $newPos++;
298
+ if ($kind === 0) {
299
+ $kind = self::EOF;
300
+ }
301
+ break;
302
+ }
303
+
304
+ switch ($state) {
305
+ case 0: // analyzing first symbol
306
+ if ($ch == '\'') {
307
+ $kind = self::STRING;
308
+ $state = 1;
309
+ $unexpectedEof = true;
310
+ }
311
+ elseif (strpos(self::$_firstIdentifierSymbol, $ch) !== false) {
312
+ $kind = self::IDENTIFIER;
313
+ $state = 3;
314
+ }
315
+ elseif (strpos(self::$_numeric, $ch) !== false) {
316
+ $kind = self::NUMBER;
317
+ $state = 4;
318
+ }
319
+ else {
320
+ foreach (self::$_tokens as $k => $v) {
321
+ if ($k != self::OPEN_FORMULA && $k != self::CLOSE_FORMULA && strpos($this->_formula, $v, $pos) === $pos) {
322
+ $kind = $k;
323
+ $text = $v;
324
+ $found = true;
325
+ $newPos++;
326
+ break;
327
+ }
328
+ }
329
+ if (!$found) {
330
+ throw new Mana_Db_Exception_Formula(Mage::helper('mana_db')->__("Unexpected character '%s'", $ch));
331
+ }
332
+ }
333
+ break;
334
+ case 1: // inside string
335
+ if ($ch == '\\') {
336
+ $state = 2;
337
+ }
338
+ elseif ($ch == '\'') {
339
+ $newPos++;
340
+ $found = true;
341
+ }
342
+ else {
343
+ $text .= $ch;
344
+ }
345
+ break;
346
+ case 2: // processing string escape symbol
347
+ switch ($ch) {
348
+ case '\'':
349
+ case '\\':
350
+ $text .= $ch;
351
+ break;
352
+ default:
353
+ $text .= '\\';
354
+ break;
355
+ }
356
+ $state = 1;
357
+ break;
358
+ case 3: // reading identifier
359
+ if (strpos(self::$_nextIdentifierSymbol, $ch) === false) {
360
+ $found = true;
361
+ }
362
+ break;
363
+ case 4: // reading number
364
+ if (strpos(self::$_numeric, $ch) === false) {
365
+ $found = true;
366
+ }
367
+ break;
368
+ }
369
+ }
370
+
371
+ $length = $newPos - $pos - 1;
372
+
373
+ if (!$text && $length) {
374
+ $text = substr($this->_formula, $pos, $length);
375
+ }
376
+ return (object)compact('kind', 'text', 'pos', 'length');
377
+ }
378
+
379
+ }
app/code/local/Mana/Db/Helper/Formula/Processor.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ abstract class Mana_Db_Helper_Formula_Processor extends Mage_Core_Helper_Abstract {
13
+ protected static $_eavEntityTypes = array();
14
+
15
+ /**
16
+ * @param Mana_Db_Model_Formula_Context $context
17
+ * @param string $field
18
+ * @return Mana_Db_Model_Formula_Expr | bool
19
+ */
20
+ public function selectField(/** @noinspection PhpUnusedParameterInspection */$context, $field) {
21
+ return false;
22
+ }
23
+
24
+ /**
25
+ * @param Mana_Db_Model_Formula_Context $context
26
+ * @param string $entity
27
+ * @return Mana_Db_Model_Formula_Entity | bool
28
+ */
29
+ public function selectEntity(/** @noinspection PhpUnusedParameterInspection */$context, $entity) {
30
+ return false;
31
+ }
32
+
33
+ abstract public function getPrimaryKey($entity);
34
+
35
+ public function getProcessor($entity) {
36
+ /* @var $dbConfig Mana_Db_Helper_Config */
37
+ $dbConfig = Mage::helper('mana_db/config');
38
+
39
+ if ($dbConfig->getScopeXml($entity)) {
40
+ return 'entity';
41
+ }
42
+ elseif ($this->getEavEntityType($entity)) {
43
+ return 'eav';
44
+ }
45
+ else {
46
+ return 'table';
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @param string $entity
52
+ * @return Mage_Eav_Model_Entity_Type | bool
53
+ */
54
+ public function getEavEntityType($entity) {
55
+ if (!isset(self::$_eavEntityTypes[$entity])) {
56
+ /* @var $entityType Mage_Eav_Model_Entity_Type */
57
+ $entityType = Mage::getModel('eav/entity_type');
58
+ $entityType->load($entity, 'entity_model');
59
+ self::$_eavEntityTypes[$entity] = $entityType->getId() ? $entityType : false;
60
+ }
61
+ return self::$_eavEntityTypes[$entity];
62
+ }
63
+ }
app/code/local/Mana/Db/Helper/Formula/Processor/Eav.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Processor_Eav extends Mana_Db_Helper_Formula_Processor {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param string $field
16
+ * @return Mana_Db_Model_Formula_Expr | bool
17
+ */
18
+ public function selectField($context, $field) {
19
+ if ($result = parent::selectField($context, $field)) {
20
+ return $result;
21
+ }
22
+
23
+ $eavEntityType = $this->getEavEntityType($context->getEntity());
24
+ /* @var $attribute Mage_Eav_Model_Entity_Attribute */
25
+ $attribute = $eavEntityType->getAttributeCollection()->getItemByColumnValue('attribute_code', $field);
26
+
27
+ if ($attribute) {
28
+ return $context->getHelper()->expr()
29
+ ->setFieldExpr($context->getAlias()->fieldExpr($context, $field))
30
+ ->setFieldName($field)
31
+ ->setType($attribute->getBackendType());
32
+ }
33
+ else {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ public function getPrimaryKey($entity) {
39
+ return 'entity_id';
40
+ }
41
+ }
app/code/local/Mana/Db/Helper/Formula/Processor/Entity.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Processor_Entity extends Mana_Db_Helper_Formula_Processor {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param string $field
16
+ * @return Mana_Db_Model_Formula_Expr | bool
17
+ */
18
+ public function selectField($context, $field) {
19
+ if ($result = parent::selectField($context, $field)) {
20
+ return $result;
21
+ }
22
+
23
+ /* @var $dbConfig Mana_Db_Helper_Config */
24
+ $dbConfig = Mage::helper('mana_db/config');
25
+
26
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
27
+ $formulaHelper = Mage::helper('mana_db/formula');
28
+
29
+ /* @var $fieldsXml Varien_Simplexml_Element */
30
+ /** @noinspection PhpUndefinedFieldInspection */
31
+ $fieldsXml = $dbConfig->getScopeXml($context->getEntity())->fields;
32
+
33
+ if (isset($fieldsXml->$field)) {
34
+ $alias = $context->getAlias();
35
+ return $context->getHelper()->expr()
36
+ ->setFieldExpr($alias->fieldExpr($context, $field))
37
+ ->setFieldName($field)
38
+ ->setType((string)$fieldsXml->$field->type);
39
+ }
40
+ else {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * @param Mana_Db_Model_Formula_Context $context
47
+ * @param string $entity
48
+ * @return Mana_Db_Model_Formula_Entity | bool
49
+ */
50
+ public function selectEntity($context, $entity) {
51
+ if ($aggregateContext = parent::selectEntity($context, $entity)) {
52
+ return $aggregateContext;
53
+ }
54
+
55
+ /* @var $dbConfig Mana_Db_Helper_Config */
56
+ $dbConfig = Mage::helper('mana_db/config');
57
+
58
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
59
+ $formulaHelper = Mage::helper('mana_db/formula');
60
+
61
+ $scopeXml = $dbConfig->getScopeXml($context->getEntity());
62
+ if ($result = $this->_selectEntityBasedOnXml($context, $scopeXml->formula->base->from, $entity, 'foreign')) {
63
+ $this->setForeignJoin($context, $result);
64
+ return $result;
65
+ }
66
+ if ($result = $this->_selectEntityBasedOnXml($context, $scopeXml->formula->base->join, $entity, 'foreign')) {
67
+ $this->setForeignJoin($context, $result);
68
+ return $result;
69
+ }
70
+ if ($result = $this->_selectEntityBasedOnXml($context, $scopeXml->formula->aggregate, $entity, 'aggregate')) {
71
+ return $result;
72
+ }
73
+ if ($result = $this->_selectEntityBasedOnXml($context, $scopeXml->formula->frontend, $entity, 'frontend')) {
74
+ return $result;
75
+ }
76
+ $field = $entity.'_id_0';
77
+ if (isset($scopeXml->fields->$field)) {
78
+ $fieldPrefix = $entity . '_id_';
79
+ /* @var $result Mana_Db_Model_Formula_Entity */
80
+ $result = Mage::getModel('mana_db/formula_entity');
81
+
82
+ $entityName = (string)$scopeXml->fields->$field->foreign->entity;
83
+ $fields = array();
84
+
85
+ $result
86
+ ->setHelper('aggregate_field')
87
+ ->setEntity($entityName)
88
+ ->setProcessor($this->getProcessor($entityName));
89
+
90
+ /* @var $core Mana_Core_Helper_Data */
91
+ $core = Mage::helper('mana_core');
92
+ $compositeAlias = array();
93
+
94
+ /* @var $joinClosure Mana_Db_Model_Formula_Closure_AggregateFieldJoin */
95
+ $joinClosure = Mage::getModel('mana_db/formula_closure_aggregateFieldJoin', compact('context', 'result'));
96
+
97
+ foreach ($scopeXml->fields->children() as $fieldName => $fieldXml) {
98
+ if ($core->startsWith($fieldName, $fieldPrefix)) {
99
+ $index = (int)substr($fieldName, strlen($fieldPrefix));
100
+ $context->getAlias()->child($formulaHelper->createAlias($entity . $index))->each($joinClosure
101
+ ->setTargetIndex($index)
102
+ ->setEntityName($entityName)
103
+ ->setFieldXml($fieldXml));
104
+ }
105
+ }
106
+ $compositeAlias = $joinClosure->getCompositeAlias();
107
+ $fields = $joinClosure->getFields();
108
+ ksort($compositeAlias);
109
+ ksort($fields);
110
+
111
+ return $result
112
+ ->setAlias($formulaHelper->createAlias($compositeAlias))
113
+ ->setAggregateFields($fields);
114
+ }
115
+
116
+ return false;
117
+ }
118
+
119
+ /**
120
+ * @param Mana_Db_Model_Formula_Context $context
121
+ * @param Mana_Db_Model_Formula_Entity $entity
122
+ */
123
+ protected function setForeignJoin($context, $entity) {
124
+ /* @var $joinClosure Mana_Db_Model_Formula_Closure_ForeignJoin */
125
+ $joinClosure = Mage::getModel('mana_db/formula_closure_foreignJoin', compact('context', 'entity'));
126
+ $entity->setForeignJoin($context->getAlias()->each($joinClosure));
127
+ }
128
+
129
+ public function getPrimaryKey($entity) {
130
+ return 'id';
131
+ }
132
+
133
+ /**
134
+ * @param Mana_Db_Model_Formula_Context $context
135
+ * @param Varien_Simplexml_Element $xml
136
+ * @param string $entity
137
+ * @param string $mode
138
+ * @return Mana_Db_Model_Formula_Entity
139
+ */
140
+ protected function _selectEntityBasedOnXml($context, $xml, $entity, $mode) {
141
+ if (isset($xml->$entity)) {
142
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
143
+ $formulaHelper = Mage::helper('mana_db/formula');
144
+
145
+ /* @var $entityXml Varien_Simplexml_Element */
146
+ /** @noinspection PhpUndefinedFieldInspection */
147
+ $entityXml = $xml->$entity;
148
+
149
+ /* @var $result Mana_Db_Model_Formula_Entity */
150
+ $result = Mage::getModel('mana_db/formula_entity');
151
+
152
+ if ($entity == 'primary') {
153
+ /* @var $dbConfig Mana_Db_Helper_Config */
154
+ $dbConfig = Mage::helper('mana_db/config');
155
+
156
+ $scopeXml = $dbConfig->getScopeXml($context->getEntity());
157
+ /** @noinspection PhpUndefinedFieldInspection */
158
+ $entityName = (string)$scopeXml->flattens;
159
+ }
160
+ else {
161
+ /** @noinspection PhpUndefinedFieldInspection */
162
+ $entityName = (string)$entityXml->entity;
163
+ }
164
+ $data = $entityXml->asArray();
165
+ $alias = $context->getAlias()->child($formulaHelper->createAlias($entity));
166
+
167
+ return $result
168
+ ->setHelper($mode)
169
+ ->setAlias($alias)
170
+ ->setEntity($entityName)
171
+ ->setProcessor($this->getProcessor($entityName))
172
+ ->addData($data ? $data : array());
173
+ }
174
+ else {
175
+ return false;
176
+ }
177
+ }
178
+ }
app/code/local/Mana/Db/Helper/Formula/Processor/System.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Processor_System extends Mana_Db_Helper_Formula_Processor {
13
+ public function getPrimaryKey($entity) {
14
+ return '';
15
+ }
16
+ }
app/code/local/Mana/Db/Helper/Formula/Processor/Table.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Processor_Table extends Mana_Db_Helper_Formula_Processor {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Context $context
15
+ * @param string $field
16
+ * @return Mana_Db_Model_Formula_Expr | bool
17
+ */
18
+ public function selectField($context, $field) {
19
+ if ($result = parent::selectField($context, $field)) {
20
+ return $result;
21
+ }
22
+
23
+ /* @var $resource Mana_Db_Resource_Formula */
24
+ $resource = Mage::getResourceSingleton('mana_db/formula');
25
+
26
+ $fields = $resource->getTableFields($context->getEntity());
27
+
28
+ if (isset($fields[$field])) {
29
+ return $context->getHelper()->expr()
30
+ ->setFieldExpr($context->getAlias()->fieldExpr($context, $field))
31
+ ->setFieldName($field)
32
+ ->setType($fields[$field]['DATA_TYPE']);
33
+ }
34
+ else {
35
+ /* @var $dbConfig Mana_Db_Helper_Config */
36
+ $dbConfig = Mage::helper('mana_db/config');
37
+
38
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
39
+ $formulaHelper = Mage::helper('mana_db/formula');
40
+
41
+ /* @var $fieldsXml Varien_Simplexml_Element */
42
+ /** @noinspection PhpUndefinedFieldInspection */
43
+ $fieldsXml = $dbConfig->getTableXml($context->getEntity())->fields;
44
+
45
+ if (isset($fieldsXml->$field)) {
46
+ $alias = $context->getAlias();
47
+ $formula = $fieldsXml->$field->formula;
48
+ $context->resetLocalAliases();
49
+
50
+ if (isset($formula->join)) {
51
+ /* @var $joinContainerXml Varien_Simplexml_Element */
52
+ $joinContainerXml = $formula->join;
53
+ foreach ($joinContainerXml->children() as $joinAlias => $definition) {
54
+ /* @var $joinClosure Mana_Db_Model_Formula_Closure_Join */
55
+ $joinClosure = Mage::getModel('mana_db/formula_closure_join', compact('context', 'definition'));
56
+ $alias
57
+ ->child($formulaHelper->createAlias($joinAlias))
58
+ ->each($joinClosure);
59
+
60
+ }
61
+ }
62
+ return $context->getHelper()->expr()
63
+ ->setFieldExpr($alias->expr($context, (string)$formula->expr))
64
+ ->setFieldName($field)
65
+ ->setType((string)$fieldsXml->$field->type);
66
+ }
67
+ else {
68
+ return false;
69
+ }
70
+ }
71
+ }
72
+
73
+ public function getPrimaryKey($entity) {
74
+ /* @var $resource Mana_Db_Resource_Formula */
75
+ $resource = Mage::getResourceSingleton('mana_db/formula');
76
+
77
+ foreach ($resource->getTableFields($entity) as $field) {
78
+ if (!empty($field['PRIMARY'])) {
79
+ return $field['COLUMN_NAME'];
80
+ }
81
+ }
82
+
83
+ return false;
84
+ }
85
+
86
+ }
app/code/local/Mana/Db/Helper/Formula/Selector.php ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Helper_Formula_Selector extends Mana_Db_Helper_Formula_Abstract {
13
+ protected $_methodPrefix = '_select';
14
+
15
+ /**
16
+ * @param Mana_Db_Model_Formula_Expr $expr
17
+ * @param string $type
18
+ * @throws Mana_Db_Exception_Formula
19
+ * @return Mana_Db_Model_Formula_Expr
20
+ */
21
+ public function cast($expr, $type) {
22
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
23
+ $formulaHelper = Mage::helper('mana_db/formula');
24
+
25
+ $sourceType = $formulaHelper->getType($expr->getType());
26
+ $targetType = $formulaHelper->getType($type);
27
+ if ($targetType == 'string') {
28
+ if ($sourceType != 'string') {
29
+ $expr = $this->expr()->setExpr("CONCAT({$expr->getExpr()})")->setType($type);
30
+ }
31
+ }
32
+ elseif ($targetType == 'int') {
33
+ if ($sourceType != 'int') {
34
+ $expr = $this->expr()->setExpr("CAST({$expr->getExpr()} AS $type)")->setType($type);
35
+ }
36
+ }
37
+
38
+ return $expr;
39
+ }
40
+
41
+ /**
42
+ * @param Mana_Db_Model_Formula_Expr $expr1
43
+ * @param Mana_Db_Model_Formula_Expr $expr2
44
+ */
45
+ public function binaryCast(&$expr1, &$expr2) {
46
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
47
+ $formulaHelper = Mage::helper('mana_db/formula');
48
+
49
+ if ($formulaHelper->getType($expr1->getType()) == 'string' || $formulaHelper->getType($expr2->getType()) == 'string') {
50
+ $expr1 = $this->cast($expr1, 'varchar(255)');
51
+ $expr2 = $this->cast($expr2, 'varchar(255)');
52
+ }
53
+ }
54
+
55
+ /**
56
+ * @param Mana_Db_Model_Formula_Context $context
57
+ * @param Mana_Db_Model_Formula_Node $formula
58
+ */
59
+ public function selectFormula(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
60
+ $expr = $this->_select($context, $formula);
61
+ if ($expr->getIsAggregate()) {
62
+ throw new Mana_Db_Exception_Formula($this->__("Aggregate field used outside of aggregate function"));
63
+ }
64
+ $expr = $this->cast($expr, $context->getField()->getType());
65
+ $expr = $this->_getOverriddenValueExpr($context, $expr->getExpr());
66
+ $context->getSelect()->columns(array($context->getField()->getName() => new Zend_Db_Expr(
67
+ $expr)));
68
+ }
69
+
70
+ public function filterFormula(/** @noinspection PhpUnusedParameterInspection */$context, $formula, $id) {
71
+ $context->getSelect()->where($this->_select($context, $formula)->getExpr() .' = ?', $id);
72
+ }
73
+
74
+ /**
75
+ * @param Mana_Db_Model_Formula_Context $context
76
+ * @param Mana_Db_Model_Formula_Node $formula
77
+ * @return Mana_Db_Model_Formula_Expr
78
+ */
79
+ protected function _select(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
80
+ return $this->_call(func_get_args());
81
+ }
82
+
83
+ /**
84
+ * @param Mana_Db_Model_Formula_Context $context
85
+ * @param mixed $value
86
+ * @throws Exception
87
+ */
88
+ public function selectValue($context, $value) {
89
+ $expr = $this->_getOverriddenValueExpr($context, $this->_getValue($context, $value));
90
+ $context->getSelect()->columns(array($context->getField()->getName() => new Zend_Db_Expr($expr)));
91
+ }
92
+
93
+ /**
94
+ * @param Mana_Db_Model_Formula_Context $context
95
+ * @param mixed $value
96
+ * @return string
97
+ */
98
+ protected function _getValue($context, $value) {
99
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
100
+ $formulaHelper = Mage::helper('mana_db/formula');
101
+
102
+ if ($formulaHelper->getType($context->getField()->getType()) == 'int') {
103
+ return $value;
104
+ }
105
+ else {
106
+ /* @var $resource Mana_Db_Resource_Formula */
107
+ $resource = Mage::getResourceSingleton('mana_db/formula');
108
+
109
+ return $resource->getReadConnection()->quote($value);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * @param Mana_Db_Model_Formula_Context $context
115
+ * @param mixed $value
116
+ * @return string
117
+ */
118
+ protected function _getDefaultValue($context) {
119
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
120
+ $formulaHelper = Mage::helper('mana_db/formula');
121
+
122
+ if ($formulaHelper->getType($context->getField()->getType()) == 'int') {
123
+ return "0";
124
+ }
125
+ else {
126
+ return "''";
127
+ }
128
+ }
129
+
130
+ /**
131
+ * @param Mana_Db_Model_Formula_Context $context
132
+ */
133
+ public function selectDefaultValue($context) {
134
+ $expr = $this->_getOverriddenValueExpr($context, $this->_getDefaultValue($context));
135
+ $context->getSelect()->columns(array($context->getField()->getName() => new Zend_Db_Expr($expr)));
136
+ }
137
+
138
+ /**
139
+ * @param Mana_Db_Model_Formula_Context $context
140
+ * @return bool
141
+ */
142
+ public function selectSystemField($context) {
143
+ if ($context->getField()->getRole() == Mana_Db_Helper_Config::ROLE_PRIMARY_KEY) {
144
+ return false;
145
+ }
146
+
147
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
148
+ $formulaHelper = Mage::helper('mana_db/formula');
149
+
150
+ $fieldExpr = "`{$context->registerAlias('primary')}`.`{$context->getField()->getName()}`";
151
+ if ($context->getField()->hasValue()) {
152
+ $context->getSelect()->columns(array($context->getField()->getName() => new Zend_Db_Expr(
153
+ "COALESCE($fieldExpr, {$this->_getValue($context, $context->getField()->getValue())})")));
154
+ }
155
+ else {
156
+ $context->getSelect()->columns(array(
157
+ $context->getField()->getName() => new Zend_Db_Expr(
158
+ "COALESCE($fieldExpr, {$this->_getDefaultValue($context)})")
159
+ ));
160
+ }
161
+ return true;
162
+ }
163
+
164
+ /**
165
+ * @param Mana_Db_Model_Formula_Context $context
166
+ * @param Mana_Db_Model_Formula_Node_Add $formula
167
+ * @throws Mana_Db_Exception_Formula
168
+ * @throws Exception
169
+ * @return Mana_Db_Model_Formula_Expr
170
+ */
171
+ protected function _selectAdd($context, $formula) {
172
+ if ($context->getIsAggregate()) {
173
+ throw new Mana_Db_Exception_Formula($this->__("You can only use aggregate function on aggregate fields"));
174
+ }
175
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
176
+ $formulaHelper = Mage::helper('mana_db/formula');
177
+
178
+ $a = $this->_select($context, $formula->a);
179
+ $b = $this->_select($context, $formula->b);
180
+ $this->binaryCast($a, $b);
181
+ switch ($formula->operator) {
182
+ case Mana_Db_Model_Formula_Node_Add::ADD:
183
+ if ($formulaHelper->getType($a->getType()) == 'string') {
184
+ return $this->expr()->setExpr("CONCAT({$a->getExpr()}, {$b->getExpr()})")->setType($a->getType());
185
+ }
186
+ else {
187
+ return $this->expr()->setExpr("{$a->getExpr()} + {$b->getExpr()}")->setType($a->getType());
188
+ }
189
+ case Mana_Db_Model_Formula_Node_Add::SUBTRACT:
190
+ if ($formulaHelper->getType($a->getType()) == 'string') {
191
+ throw new Mana_Db_Exception_Formula($this->__("'%s' operator is not supported on fields of %s type", '-', 'string'));
192
+ }
193
+ else {
194
+ return $this->expr()->setExpr("{$a->getExpr()} - {$b->getExpr()}")->setType($a->getType());
195
+ }
196
+ default:
197
+ throw new Exception('Not implemented');
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Converts parsed field expression $formula into SQL column expression. If needed, introduces new joins in current
203
+ * SELECT.
204
+ *
205
+ * @param Mana_Db_Model_Formula_Context $originalContext
206
+ * @param Mana_Db_Model_Formula_Node_Field $formula
207
+ * @throws Mana_Db_Exception_Formula
208
+ * @return Mana_Db_Model_Formula_Expr
209
+ *
210
+ * Field expression looks like "a.b.c.d".
211
+ *
212
+ * The last identifier is field name (in case of system configuration fields field
213
+ * name consists of 3 last identifiers "b.c.d").
214
+ *
215
+ * All the identifiers before field name ("a.b.c") are entity names. Each of them is either foreign entities,
216
+ * frontend entities or aggregate entities:
217
+ * * foreign entity example: catalog/product for sales/order_item. For foreign entity LEFT or INNER JOIN is added based on
218
+ * foreign key and primary key relationship and column using that foreign key is added.
219
+ * * aggregate entity example sales/order_item for sales/order. Fields from aggregate entities are returned to
220
+ * parent formula node which is expected to be an aggregate formula such as GLUE, SUM, MAX. In final SQL aggregate
221
+ * fields appear as sub selects, so sub select object is returned for aggregate field as well.
222
+ * * frontend entity example: catalog/category for mana_attributepage/page (in database there is no relation to
223
+ * frontend entity, but it is known when calculated in frontend, typically from Mage::registry()). Such entities
224
+ * are left unprocessed (with type = 'formula,int' or 'formula,string') and are only processed before displaying
225
+ * data in frontend. Frontend entities can appear in identifier chain in first position only.
226
+ *
227
+ * How these entity types behave in identifier chain:
228
+ * * foreign entity:
229
+ * * no aggregate or frontend entity met: adds JOIN to main SELECT and changes current entity in main context
230
+ * * inside aggregate, no frontend entity met: adds JOIN to aggregate SUBSELECT and changes current entity in
231
+ * subselect context
232
+ * * inside frontend entity: changes current entity in main context
233
+ * * aggregate entity:
234
+ * * no frontend entity met: creates aggregate context to collect aggregate subselect
235
+ * * inside frontend entity: changes current entity in main context
236
+ * * frontend entity: sets a flag for processing frontend entity
237
+ */
238
+ protected function _selectField($originalContext, $formula) {
239
+ if ($formula->identifiers[0] == 'this') {
240
+ if (count($formula->identifiers) == 1) {
241
+ throw new Mana_Db_Exception_Formula(Mage::helper('mana_db')->__("'%s' is entity, but field expected", implode('.', $formula->identifiers)));
242
+ }
243
+ $formula->identifiers = array_slice($formula->identifiers, 1);
244
+ if (count($formula->identifiers) == 1) {
245
+ /* @var $identifier Mana_Db_Model_Formula_Node_Identifier */
246
+ $identifier = Mage::getModel('mana_db/formula_node_identifier');
247
+ $identifier->identifier = $formula->identifiers[0];
248
+ $this->_selectIdentifier($originalContext, $identifier);
249
+ }
250
+ }
251
+ $context = clone $originalContext;
252
+ $result = $this->_selectFieldRecursively($context, $formula, 0);
253
+ $originalContext->copyAliases($context);
254
+ return $result;
255
+ }
256
+
257
+ /**
258
+ * Converts parsed field expression $formula into SQL column expression. If needed, introduces new joins in current
259
+ * SELECT.
260
+ *
261
+ * @param Mana_Db_Model_Formula_Context $context
262
+ * @param Mana_Db_Model_Formula_Node_Field $formula
263
+ * @param int $index
264
+ * @throws Mana_Db_Exception_Formula
265
+ * @return Mana_Db_Model_Formula_Expr
266
+ */
267
+ protected function _selectFieldRecursively($context, $formula, $index) {
268
+ $processor = $context->getProcessor();
269
+ if (isset($formula->identifiers[$index])) {
270
+ $identifier = $formula->identifiers[$index];
271
+ }
272
+ else {
273
+ throw new Mana_Db_Exception_Formula(Mage::helper('mana_db')->__("'%s' is entity, but field expected", implode('.', $formula->identifiers)));
274
+ }
275
+
276
+ if ($result = $processor->selectField($context, implode('.', array_slice($formula->identifiers, $index)))) {
277
+ $context->getEntityHelper()->selectField($context, $formula, $result);
278
+ return $result;
279
+ }
280
+ else {
281
+ if ($entity = $processor->selectEntity($context, $identifier)) {
282
+ $entity->getHelper()->select($context, $entity);
283
+ $result = $this->_selectFieldRecursively($context, $formula, ++$index);
284
+ $entity->getHelper()->endSelect($context, $entity);
285
+ return $result;
286
+ }
287
+ elseif ($index == 0) {
288
+ $context->setEntity($context->getPrimaryEntity());
289
+ return $this->_selectFieldRecursively($context, $formula, $index);
290
+ }
291
+ else {
292
+ throw new Mana_Db_Exception_Formula($this->__("Unknown field or entity '%s'", $identifier));
293
+ }
294
+ }
295
+ }
296
+
297
+ /**
298
+ * @param Mana_Db_Model_Formula_Context $context
299
+ * @param Mana_Db_Model_Formula_Node_FormulaExpr $formula
300
+ * @return Mana_Db_Model_Formula_Expr
301
+ */
302
+ protected function _selectFormulaExpr($context, $formula) {
303
+ $parts = array();
304
+ foreach ($formula->parts as $part) {
305
+ $parts[] = $this->cast($this->_select($context, $part), 'varchar(255)')->getExpr();
306
+ }
307
+ $parts = implode(', ', $parts);
308
+ return $this->expr()->setExpr("CONCAT($parts)")->setType('varchar(255)');
309
+ }
310
+
311
+ /**
312
+ * @param Mana_Db_Model_Formula_Context $context
313
+ * @param Mana_Db_Model_Formula_Node_FunctionCall $formula
314
+ * @return Mana_Db_Model_Formula_Expr
315
+ */
316
+ protected function _selectFunctionCall($context, $formula) {
317
+ $args = array();
318
+ foreach ($formula->args as $arg) {
319
+ $args[] = $this->_select($context, $arg);
320
+ }
321
+
322
+ return $this->getFunction($formula->name)->select($context, $args);
323
+ }
324
+
325
+ /**
326
+ * @param Mana_Db_Model_Formula_Context $context
327
+ * @param Mana_Db_Model_Formula_Node_Identifier $formula
328
+ * @throws Mana_Db_Exception_Formula
329
+ * @return Mana_Db_Model_Formula_Expr
330
+ */
331
+ protected function _selectIdentifier($context, $formula) {
332
+ if ($result = $context->getProcessor()->selectField($context, $formula->identifier)) {
333
+ $context->getEntityHelper()->selectField($context, null, $result);
334
+
335
+ return $result;
336
+ }
337
+ else {
338
+ throw new Mana_Db_Exception_Formula($this->__("Can't evaluate '%s'", $formula->identifier));
339
+ }
340
+ }
341
+
342
+ /**
343
+ * @param Mana_Db_Model_Formula_Context $context
344
+ * @param Mana_Db_Model_Formula_Node_Multiply $formula
345
+ * @throws Mana_Db_Exception_Formula
346
+ * @throws Exception
347
+ * @return Mana_Db_Model_Formula_Expr
348
+ */
349
+ protected function _selectMultiply($context, $formula) {
350
+ if ($context->getIsAggregate()) {
351
+ throw new Mana_Db_Exception_Formula($this->__("You can only use aggregate function on aggregate fields"));
352
+ }
353
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
354
+ $formulaHelper = Mage::helper('mana_db/formula');
355
+
356
+ $a = $this->_select($context, $formula->a);
357
+ $b = $this->_select($context, $formula->b);
358
+ $this->binaryCast($a, $b);
359
+ switch ($formula->operator) {
360
+ case Mana_Db_Model_Formula_Node_Multiply::MULTIPLY:
361
+ if ($formulaHelper->getType($a->getType()) == 'string') {
362
+ throw new Mana_Db_Exception_Formula($this->__("'%s' operator is not supported on fields of %s type", '*', 'string'));
363
+ }
364
+ else {
365
+ return $this->expr()->setExpr("{$a->getExpr()} * {$b->getExpr()}")->setType($a->getType());
366
+ }
367
+ case Mana_Db_Model_Formula_Node_Multiply::DIVIDE:
368
+ if ($formulaHelper->getType($a->getType()) == 'string') {
369
+ throw new Mana_Db_Exception_Formula($this->__("'%s' operator is not supported on fields of %s type", '/', 'string'));
370
+ }
371
+ else {
372
+ return $this->expr()->setExpr("{$a->getExpr()} / {$b->getExpr()}")->setType($a->getType());
373
+ }
374
+ default:
375
+ throw new Exception('Not implemented');
376
+ }
377
+ }
378
+
379
+ /**
380
+ * @param Mana_Db_Model_Formula_Context $context
381
+ * @param Mana_Db_Model_Formula_Node_NullValue $formula
382
+ * @return Mana_Db_Model_Formula_Expr
383
+ */
384
+ protected function _selectNullValue(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
385
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
386
+ $formulaHelper = Mage::helper('mana_db/formula');
387
+
388
+ $type = $context->getField()->getType();
389
+ $formulaType = $formulaHelper->getType($type);
390
+ if ($formulaType == 'int') {
391
+ return $this->expr()->setExpr("0")->setType($type);
392
+ }
393
+ else {
394
+ return $this->expr()->setExpr("''")->setType($type);
395
+ }
396
+ }
397
+
398
+ /**
399
+ * @param Mana_Db_Model_Formula_Context $context
400
+ * @param Mana_Db_Model_Formula_Node_NumberConstant $formula
401
+ * @return Mana_Db_Model_Formula_Expr
402
+ */
403
+ protected function _selectNumberConstant(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
404
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
405
+ $formulaHelper = Mage::helper('mana_db/formula');
406
+
407
+ return $this->expr()->setExpr("{$formula->value}")->setType($formulaHelper->getMinimumNumericType($formula->value));
408
+ }
409
+
410
+ /**
411
+ * @param Mana_Db_Model_Formula_Context $context
412
+ * @param Mana_Db_Model_Formula_Node_StringConstant $formula
413
+ * @return Mana_Db_Model_Formula_Expr
414
+ */
415
+ protected function _selectStringConstant(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
416
+ return $this->expr()->setExpr("'{$formula->value}'")->setType('varchar(255)');
417
+ }
418
+
419
+ /**
420
+ * @param Mana_Db_Model_Formula_Context $context
421
+ * @param Mana_Db_Model_Formula_Node $formula
422
+ */
423
+ protected function _selectNode(/** @noinspection PhpUnusedParameterInspection */$context, $formula) {
424
+ // all future nodes ignored
425
+ }
426
+
427
+ /**
428
+ * @param Mana_Db_Model_Formula_Context $context
429
+ * @param string $defaultExpr
430
+ * @return string
431
+ */
432
+ protected function _getOverriddenValueExpr($context, $defaultExpr) {
433
+ if (($no = $context->getField()->getNo()) !== null) {
434
+ $overriddenExpr = "`p`.`{$context->getField()->getName()}`";
435
+ if ($overriddenExpr != $defaultExpr) {
436
+ /* @var $db Mana_Db_Helper_Data */
437
+ $db = Mage::helper('mana_db');
438
+
439
+ $condition = "`p`.`default_mask{$db->getMaskIndex($no)}` & {$db->getMask($no)} = {$db->getMask($no)}";
440
+ return "IF ($condition, $overriddenExpr, $defaultExpr)";
441
+ }
442
+ else {
443
+ return $overriddenExpr;
444
+ }
445
+ }
446
+ else {
447
+ return $defaultExpr;
448
+ }
449
+ }
450
+ }
app/code/local/Mana/Db/Model/Entity.php ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method string getScopeName()
11
+ * @method int getEditStatus()
12
+ * @method Mana_Db_Model_Entity setEditStatus(int $value)
13
+ * @method Mana_Db_Model_Entity setEditSessionId(int $value)
14
+ * @method Mana_Db_Model_Entity setEditMassaction(int $value)
15
+ * @method string getDefaultFormulas()
16
+ * @method int getStoreId()
17
+ * @method Mana_Db_Model_Entity setStoreId(int $value)
18
+ * @method int getGlobalId()
19
+ * @method Mana_Seo_Model_Schema setGlobalId(int $value)
20
+ * @method int getPrimaryId()
21
+ * @method Mana_Seo_Model_Schema setPrimaryId(int $value)
22
+ * @method int getPrimaryGlobalId()
23
+ * @method Mana_Seo_Model_Schema setPrimaryGlobalId(int $value)
24
+ */
25
+ class Mana_Db_Model_Entity extends Mage_Core_Model_Abstract {
26
+ protected $_scope;
27
+ protected $_isIndexingDisabled = false;
28
+ protected $_jsons;
29
+
30
+ public function __construct($data = null) {
31
+ if (is_array($data)) {
32
+ if (isset($data['scope'])) {
33
+ $this->_scope = $data['scope'];
34
+ unset($data['scope']);
35
+ }
36
+ }
37
+
38
+ parent::__construct($data);
39
+ }
40
+
41
+ public function loadForStore($id, $storeId, $fieldName = 'global_id') {
42
+ $this->_getResource()->loadForStore($this, $id, $storeId, $fieldName);
43
+ $this->_afterLoad();
44
+ $this->setOrigData();
45
+ $this->_hasDataChanges = false;
46
+
47
+ return $this;
48
+ }
49
+
50
+ protected function _construct() {
51
+ $this->_initScope();
52
+ }
53
+
54
+ protected function _initScope() {
55
+ /* @var $db Mana_Db_Helper_Data */
56
+ $db = Mage::helper('mana_db');
57
+
58
+ $this->_init($db->getScopedName($this->_scope), 'id');
59
+
60
+ return $this;
61
+ }
62
+
63
+ protected function _getResource() {
64
+ /* @var $db Mana_Db_Helper_Data */
65
+ $db = Mage::helper('mana_db');
66
+
67
+ if (empty($this->_resourceName)) {
68
+ Mage::throwException(Mage::helper('core')->__('Resource is not set.'));
69
+ }
70
+
71
+ return $db->getResourceSingleton($this->_resourceName);
72
+ }
73
+
74
+ public function getScope() {
75
+ return $this->_scope;
76
+ }
77
+
78
+ public function assignDefaultValues() {
79
+ $this->setDummy(true);
80
+ return $this;
81
+ }
82
+
83
+ public function addGridCellData($cells) {
84
+ foreach ($cells as $column => $cell) {
85
+ $this->overrideData($column, $cell['value']);
86
+ }
87
+ return $this;
88
+ }
89
+
90
+ public function loadEdited($id, $sessionId) {
91
+ $this->_beforeLoad($id);
92
+
93
+ /* @var $resource Mana_Db_Resource_Entity */
94
+ $resource = $this->_getResource();
95
+ $resource->loadEdited($this, $id, $sessionId);
96
+
97
+ $this->_afterLoad();
98
+ $this->setOrigData();
99
+ $this->_hasDataChanges = false;
100
+ return $this;
101
+ }
102
+
103
+ public function disableIndexing() {
104
+ $this->_isIndexingDisabled = true;
105
+ return $this;
106
+ }
107
+
108
+ public function enableIndexing() {
109
+ $this->_isIndexingDisabled = false;
110
+ return $this;
111
+ }
112
+
113
+ public function updateIndexes() {
114
+ return $this;
115
+ }
116
+
117
+ public function validate($dataSource) {
118
+ /* @var $dbConfig Mana_Db_Helper_Config */
119
+ $dbConfig = Mage::helper('mana_db/config');
120
+
121
+ foreach ($dbConfig->getScopeValidators($this->_scope) as $validator) {
122
+ call_user_func(array($this, $validator->getName()), $dataSource);
123
+ }
124
+
125
+ foreach ($dbConfig->getScopeFields($this->_scope) as $fieldXml) {
126
+ foreach ($dbConfig->getFieldValidators($fieldXml) as $validator) {
127
+ call_user_func(array($this, $validator->getName()), $dataSource, $fieldXml->getName());
128
+ }
129
+ }
130
+ return $this;
131
+ }
132
+
133
+ public function postValidate($dataSource) {
134
+ /* @var $dbConfig Mana_Db_Helper_Config */
135
+ $dbConfig = Mage::helper('mana_db/config');
136
+
137
+ foreach ($dbConfig->getScopePostValidators($this->_scope) as $validator) {
138
+ call_user_func(array($this, $validator->getName()), $dataSource);
139
+ }
140
+
141
+ foreach ($dbConfig->getScopeFields($this->_scope) as $fieldXml) {
142
+ foreach ($dbConfig->getFieldPostValidators($fieldXml) as $validator) {
143
+ call_user_func(array($this, $validator->getName()), $dataSource, $fieldXml->getName());
144
+ }
145
+ }
146
+
147
+ return $this;
148
+ }
149
+
150
+ public function isNotEmpty($dataSource, $field) {
151
+ if (!$this->getData($field)) {
152
+ throw new Mana_Db_Exception_Validation(Mage::helper('mana_db')->__("Please fill in '%s'.", $dataSource->getLabel($this->getScope(), $field)));
153
+ }
154
+ }
155
+
156
+ public function getJson($key) {
157
+ if (!$this->_jsons) {
158
+ $this->_jsons = array();
159
+ }
160
+ if (!isset($this->_jsons[$key])) {
161
+ $data = $this->getData($key);
162
+ if (empty($data)) {
163
+ $this->_jsons[$key] = array();
164
+ }
165
+ else {
166
+ $this->_jsons[$key] = json_decode($data, true);
167
+ if (is_null($this->_jsons[$key])) {
168
+ throw new Exception(json_last_error());
169
+ }
170
+ }
171
+ }
172
+
173
+ return $this->_jsons[$key];
174
+ }
175
+
176
+ public function overrideData($key, $value = null) {
177
+ $this->setData($key, $value);
178
+
179
+ if (is_array($key)) {
180
+ foreach (array_keys($key) as $aKey) {
181
+ $this->_setDefaultMask($aKey, true);
182
+ }
183
+ }
184
+ else {
185
+ $this->_setDefaultMask($key, true);
186
+ }
187
+ return $this;
188
+ }
189
+
190
+ protected function _setDefaultMask($key, $value) {
191
+ /* @var $dbConfig Mana_Db_Helper_Config */
192
+ $dbConfig = Mage::helper('mana_db/config');
193
+
194
+ if (($field = $dbConfig->getScopeField($this->_scope, $key)) && isset($field->no)) {
195
+ /* @var $db Mana_Db_Helper_Data */
196
+ $db = Mage::helper('mana_db');
197
+
198
+ $no = (int)(string)$field->no;
199
+ $maskIndex = $db->getMaskIndex($no);
200
+ $mask = isset($this->_data['default_mask' . $maskIndex]) ? $this->_data['default_mask' . $maskIndex] : 0;
201
+ if ($value) {
202
+ $mask = $mask | $db->getMask($no);
203
+ }
204
+ else {
205
+ $mask = $mask & ~$db->getMask($no);
206
+ }
207
+ $this->setData('default_mask' . $maskIndex, $mask);
208
+ }
209
+ }
210
+
211
+ public function useDefaultData($key) {
212
+ if (is_array($key)) {
213
+ foreach ($key as $aKey) {
214
+ $this->_setDefaultMask($aKey, false);
215
+ }
216
+ }
217
+ else {
218
+ $this->_setDefaultMask($key, false);
219
+ }
220
+
221
+ return $this;
222
+ }
223
+
224
+ public function isUsingDefaultData($key, $in = '_data') {
225
+ if (($field = $this->dbConfigHelper()->getScopeField($this->_scope, $key)) && isset($field->no)) {
226
+ /* @var $db Mana_Db_Helper_Data */
227
+ $db = Mage::helper('mana_db');
228
+
229
+ $no = (int)(string)$field->no;
230
+ $maskIndex = $db->getMaskIndex($no);
231
+ $data = $this->$in;
232
+ $mask = isset($data['default_mask' . $maskIndex]) ? $data['default_mask' . $maskIndex] : 0;
233
+ return !($mask & $db->getMask($no));
234
+ }
235
+ return false;
236
+ }
237
+
238
+ public function isDefaultable($key) {
239
+ return ($field = $this->dbConfigHelper()->getScopeField($this->_scope, $key)) && isset($field->no);
240
+ }
241
+
242
+ public function getFieldsXml() {
243
+ return $this->dbConfigHelper()->getScopeFields($this->_scope);
244
+ }
245
+
246
+ public function getDefaultableFields() {
247
+ $result = array();
248
+ foreach ($this->getFieldsXml() as $fieldXml) {
249
+ if (isset($fieldXml->no)) {
250
+ $result[] = (string)$fieldXml->name;
251
+ }
252
+ }
253
+ return $result;
254
+ }
255
+
256
+ public function __call($method, $args) {
257
+ if (substr($method, 0, 8) == 'override') {
258
+ //Varien_Profiler::start('SETTER: '.get_class($this).'::'.$method);
259
+ $key = $this->_underscore(substr($method, 8));
260
+ $result = $this->overrideData($key, isset($args[0]) ? $args[0] : null);
261
+
262
+ //Varien_Profiler::stop('SETTER: '.get_class($this).'::'.$method);
263
+ return $result;
264
+ }
265
+ return parent::__call($method, $args);
266
+ }
267
+
268
+ /**
269
+ * Init indexing process after category data commit
270
+ *
271
+ * @return Mage_Catalog_Model_Category
272
+ */
273
+ public function afterCommitCallback() {
274
+ parent::afterCommitCallback();
275
+ if (!Mage::registry('m_prevent_indexing_on_save')) {
276
+ $this->getIndexerSingleton()->processEntityAction($this, $this->getScope(), Mage_Index_Model_Event::TYPE_SAVE);
277
+ }
278
+ return $this;
279
+ }
280
+
281
+ #region Dependencies
282
+ /**
283
+ * @return Mana_Db_Helper_Config
284
+ */
285
+ public function dbConfigHelper() {
286
+ return Mage::helper('mana_db/config');
287
+ }
288
+
289
+ /**
290
+ * @return Mage_Index_Model_Indexer
291
+ */
292
+ public function getIndexerSingleton() {
293
+ return Mage::getSingleton('index/indexer');
294
+ }
295
+ #endregion
296
+ }
app/code/local/Mana/Db/Model/Entity/Indexer.php ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Entity_Indexer extends Mana_Core_Model_Indexer {
13
+ protected $_code = 'mana_db';
14
+ protected $_standardEventEntities = array(
15
+ 'core_store' => 'core/store',
16
+ );
17
+ /**
18
+ * @var Varien_Object[]
19
+ */
20
+ protected $_matchedEvents = array();
21
+
22
+ public function matchEvent(Mage_Index_Model_Event $event) {
23
+ /* @var $object Mana_Db_Model_Entity */
24
+ $object = $event->getData('data_object');
25
+ $key = $event->getData('entity').'-'.$object->getId();
26
+ if (!isset($this->_matchedEvents[$key])) {
27
+ $this->_matchedEvents[$key] = $this->_matchEntity($event, $object);
28
+ }
29
+ return $this->_matchedEvents[$key]->getData('is_matching');
30
+ }
31
+
32
+ /**
33
+ * @param Mage_Index_Model_Event $event
34
+ * @param Mana_Db_Model_Entity $object
35
+ * @return Varien_Object
36
+ */
37
+ protected function _matchEntity($event, /** @noinspection PhpUnusedParameterInspection */ $object) {
38
+ $result = new Varien_Object(array('is_matching' => false));
39
+ $entityFilters = array();
40
+ $eventEntity = $event->getData('entity');
41
+ if (isset($this->_standardEventEntities[$eventEntity])) {
42
+ $eventEntity = $this->_standardEventEntities[$eventEntity];
43
+ }
44
+ foreach ($this->_sortTargetsByDependency() as $target) {
45
+ if ($entity = (string)$target->entity) {
46
+ foreach ($this->dbConfigHelper()->getEntityXml($entity)->scopes->children() as $scope) {
47
+ if (isset($scope->flattens)) {
48
+ if ($formula = $this->_findEntityFilterFormula($entity . '/' . $scope->getName(), $eventEntity)) {
49
+ $result->setData('is_matching', true);
50
+ $entityFilters[$entity . '/' . $scope->getName()] = $formula;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ $result->setData('entity_filters', $entityFilters);
58
+ return $result;
59
+ }
60
+ protected function _findEntityFilterFormula($entity, $what) {
61
+ return $this->_findEntityFilterFormulaRecursively($entity, $what, '');
62
+ }
63
+
64
+ protected function _findEntityFilterFormulaRecursively($entity, $what, $formula) {
65
+ if (!($scopeXml = $this->dbConfigHelper()->getScopeXml($entity))) {
66
+ return false;
67
+ }
68
+
69
+ if (!isset($scopeXml->formula)) {
70
+ return false;
71
+ }
72
+
73
+ foreach ($scopeXml->formula->children() as $selectXml) {
74
+ if (isset($selectXml->from)) {
75
+ $fromXml = $selectXml->from;
76
+ foreach ($fromXml->children() as $alias => $definition) {
77
+ $sourceEntity = $alias == 'primary' ? (string)$scopeXml->flattens : (string)$definition->entity;
78
+ if ($sourceEntity == $what) {
79
+ return $formula . $alias . '.' . $this->_getPrimaryKey($sourceEntity);
80
+ }
81
+ else {
82
+ if ($result = $this->_findEntityFilterFormulaRecursively($sourceEntity, $what, $formula . $alias . '.')) {
83
+ return $result;
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ if (isset($selectXml->join)) {
90
+ $joinXml = $selectXml->join;
91
+ /* @var $joinXml SimpleXMLElement */
92
+ foreach ($joinXml->children() as $alias => $definition) {
93
+ $sourceEntity = $alias == 'primary' ? (string)$scopeXml->flattens : (string)$definition->entity;
94
+ if ($sourceEntity == $what) {
95
+ return $formula . $alias . '.' . $this->_getPrimaryKey($sourceEntity);
96
+ }
97
+ else {
98
+ if ($result = $this->_findEntityFilterFormulaRecursively($sourceEntity, $what, $formula . $alias . '.')) {
99
+ return $result;
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+
108
+ protected function _getPrimaryKey($entity) {
109
+ if ($this->dbConfigHelper()->getScopeXml($entity)) {
110
+ return 'id';
111
+ }
112
+ else {
113
+ return $this->tableProcessorHelper()->getPrimaryKey($entity);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Register indexer required data inside event object
119
+ *
120
+ * @param Mage_Index_Model_Event $event
121
+ */
122
+ protected function _registerEvent(Mage_Index_Model_Event $event) {
123
+ /* @var $object Mana_Db_Model_Entity */
124
+ $object = $event->getData('data_object');
125
+ $key = $event->getData('entity') . '-' . $object->getId();
126
+ if (isset($this->_matchedEvents[$key])) {
127
+ $event
128
+ ->addNewData('entity_filters', $this->_matchedEvents[$key]->getData('entity_filters'))
129
+ ->addNewData('entity_filter_id', $object->getId());
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Process event based on event state data
135
+ *
136
+ * @param Mage_Index_Model_Event $event
137
+ */
138
+ protected function _processEvent(Mage_Index_Model_Event $event) {
139
+ $this->process($event->getNewData());
140
+ }
141
+
142
+ /**
143
+ * @return Mana_Db_Resource_Entity_Indexer
144
+ */
145
+ protected function _getResource() {
146
+ if (!$this->_resourceName) {
147
+ $this->_resourceName = (string)$this->getXml()->resource;
148
+ }
149
+ if (!$this->_resourceName) {
150
+ $this->_resourceName = 'mana_db/entity_indexer';
151
+ }
152
+
153
+ return Mage::getResourceSingleton($this->_resourceName);
154
+ }
155
+
156
+ public function reindexAll() {
157
+ $this->process(array(
158
+ "{$this->getCode()}_reindex_all" => true
159
+ ));
160
+
161
+ return $this;
162
+ }
163
+
164
+ /**
165
+ * @param array $options
166
+ * @return array
167
+ */
168
+ protected function _prepareOptions($options) {
169
+ /* @var $core Mana_Core_Helper_Data */
170
+ $core = Mage::helper('mana_core');
171
+
172
+ $result = array();
173
+ foreach ($options as $key => $value) {
174
+ if ($core->startsWith($key, $this->getCode() . '_')) {
175
+ $key = substr($key, strlen($this->getCode() . '_'));
176
+ }
177
+ $result[$key] = $value;
178
+ }
179
+
180
+ $result = array_merge(array(
181
+ 'reindex_all' => false,
182
+ ), $result);
183
+
184
+ return $result;
185
+ }
186
+
187
+ /**
188
+ * @throws Exception
189
+ * @return Varien_Simplexml_Element[]
190
+ */
191
+ protected function _sortTargetsByDependency() {
192
+ $targets = array();
193
+
194
+ if ($this->getXml()->targets) {
195
+ foreach ($this->getXml()->targets->children() as $target) {
196
+ $targets[] = $target;
197
+ }
198
+ }
199
+
200
+ if ($count = count($targets)) {
201
+ $orders = array();
202
+ for ($position = 0; $position < $count; $position++) {
203
+ $found = false;
204
+ foreach ($targets as $target) {
205
+ if (!isset($orders[$target->getName()])) {
206
+ $hasUnresolvedDependency = false;
207
+ if (isset($target->depends)) {
208
+ foreach ($target->depends->children() as $dependency) {
209
+ if (!isset($orders[$dependency->getName()])) {
210
+ // $dependency not yet sorted so $module should wait until that happens
211
+ $hasUnresolvedDependency = true;
212
+ break;
213
+ }
214
+ }
215
+ }
216
+ if (!$hasUnresolvedDependency) {
217
+ $found = $target;
218
+ break;
219
+ }
220
+ }
221
+ }
222
+ if ($found) {
223
+ $found->sort_order = $orders[$found->getName()] = count($orders);
224
+ }
225
+ else {
226
+ $circular = array();
227
+ foreach ($targets as $target) {
228
+ if (!isset($orders[$target->getName()])) {
229
+ $circular[] = $target->getName();
230
+ }
231
+ }
232
+ throw new Exception(sprintf(
233
+ "Circular target dependency is not allowed. " .
234
+ "Please check these targets: %s.",
235
+ implode(', ', $circular)
236
+ ));
237
+ }
238
+ }
239
+ uasort($targets, array($this, '_compareBySortOrder'));
240
+ }
241
+
242
+ return $targets;
243
+ }
244
+
245
+ protected function _compareBySortOrder($a, $b) {
246
+ if (((int)(string)$a->sort_order) < ((int)(string)$b->sort_order)) return -1;
247
+ if (((int)(string)$a->sort_order) > ((int)(string)$b->sort_order)) return 1;
248
+
249
+ return 0;
250
+ }
251
+
252
+ /**
253
+ * @param Varien_Simplexml_Element $target
254
+ * @param array $options
255
+ */
256
+ protected function _processTarget($target, $options) {
257
+ $entity = (string)$target->entity;
258
+
259
+ /* @var $globalScope Varien_Simplexml_Element */
260
+ $globalScope = null;
261
+ /* @var $storeScope Varien_Simplexml_Element */
262
+ $storeScope = null;
263
+
264
+ foreach ($this->dbConfigHelper()->getEntityXml($entity)->scopes->children() as $scope) {
265
+ if (isset($scope->flattens)) {
266
+ $flattenedScope = (string)$scope->flattens;
267
+ if (isset($this->dbConfigHelper()->getScopeXml($flattenedScope)->store_specifics_for)) {
268
+ $storeScope = $scope;
269
+ }
270
+ else {
271
+ $globalScope = $scope;
272
+ }
273
+ }
274
+ }
275
+
276
+ if ($globalScope) {
277
+ $this->_flattenGlobalScope($target, $globalScope, $options);
278
+ }
279
+ if ($storeScope) {
280
+ $this->_flattenStoreScope($target, $storeScope, $options);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * @param Varien_Simplexml_Element $target
286
+ * @param Varien_Simplexml_Element $scope
287
+ * @param array $options
288
+ */
289
+ protected function _flattenGlobalScope($target, $scope, $options) {
290
+ $this->_getResource()->flattenScope($this, $target, $scope, $options);
291
+ }
292
+
293
+ /**
294
+ * @param Varien_Simplexml_Element $target
295
+ * @param Varien_Simplexml_Element $scope
296
+ * @param array $options
297
+ */
298
+ protected function _flattenStoreScope($target, $scope, $options) {
299
+ $this->_getResource()->flattenScope($this, $target, $scope, $options);
300
+ }
301
+
302
+ /**
303
+ * @param array $options
304
+ */
305
+ public function process($options = array()) {
306
+ $options = $this->_prepareOptions($options);
307
+ $targets = $this->_sortTargetsByDependency();
308
+ foreach ($targets as $target) {
309
+ $this->_processTarget($target, $options);
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Retrieve Indexer description
315
+ *
316
+ * @return string
317
+ */
318
+ public function getDescription() {
319
+ $targets = $this->_sortTargetsByDependency();
320
+ $descriptions = array();
321
+ foreach ($targets as $target) {
322
+ if (isset($target->description)) {
323
+ $descriptions[] = (string)$target->description;
324
+ }
325
+ }
326
+
327
+ $result = parent::getDescription();
328
+ if (count($descriptions)) {
329
+ $result .= ': '.implode(', ', $descriptions);
330
+ }
331
+ return $result;
332
+ }
333
+
334
+ #region Dependencies
335
+ /**
336
+ * @return Mana_Db_Helper_Config
337
+ */
338
+ public function dbConfigHelper() {
339
+ return Mage::helper('mana_db/config');
340
+ }
341
+
342
+ /**
343
+ * @return Mana_Db_Helper_Formula_Processor_Table
344
+ */
345
+ public function tableProcessorHelper() {
346
+ return Mage::helper('mana_db/formula_processor_table');
347
+ }
348
+ #endregion
349
+ }
app/code/local/Mana/Db/Model/Formula/Alias.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ abstract class Mana_Db_Model_Formula_Alias {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Alias $suffix
15
+ * @return Mana_Db_Model_Formula_Alias
16
+ */
17
+ abstract public function child($suffix);
18
+
19
+ /**
20
+ * @param Mana_Db_Model_Formula_Alias_Single $prefix
21
+ * @return Mana_Db_Model_Formula_Alias
22
+ */
23
+ abstract public function singleChild($prefix);
24
+
25
+ /**
26
+ * @param Mana_Db_Model_Formula_Alias_Array $prefix
27
+ * @return Mana_Db_Model_Formula_Alias
28
+ */
29
+ abstract public function arrayChild($prefix);
30
+
31
+ /**
32
+ * @param Mana_Db_Model_Formula_Closure $closure
33
+ * @return mixed
34
+ */
35
+ abstract public function each($closure);
36
+
37
+ /**
38
+ * @param mixed $index
39
+ * @return string
40
+ */
41
+ abstract public function asString($index);
42
+
43
+ /**
44
+ * @param Mana_Db_Model_Formula_Context $context
45
+ * @param string $field
46
+ * @return string | string[]
47
+ */
48
+ public function fieldExpr($context, $field) {
49
+ return $this->expr($context, "{{= context.$field }}");
50
+ }
51
+
52
+ /**
53
+ * @param Mana_Db_Model_Formula_Context $context
54
+ * @param string $expr
55
+ * @return string | string[]
56
+ */
57
+ public abstract function expr($context, $expr);
58
+ }
app/code/local/Mana/Db/Model/Formula/Alias/Array.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Alias_Array extends Mana_Db_Model_Formula_Alias {
13
+ /**
14
+ * @var string[]
15
+ */
16
+ protected $_aliases;
17
+
18
+ public function __construct($aliases) {
19
+ $this->_aliases = $aliases;
20
+ }
21
+
22
+ public function getAliases() {
23
+ return $this->_aliases;
24
+ }
25
+
26
+ /**
27
+ * @param Mana_Db_Model_Formula_Alias $suffix
28
+ * @return Mana_Db_Model_Formula_Alias
29
+ */
30
+ public function child($suffix) {
31
+ return $suffix->arrayChild($this);
32
+ }
33
+
34
+ /**
35
+ * @param Mana_Db_Model_Formula_Alias_Single $prefix
36
+ * @return Mana_Db_Model_Formula_Alias
37
+ */
38
+ public function singleChild($prefix) {
39
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
40
+ $formulaHelper = Mage::helper('mana_db/formula');
41
+
42
+ $aliases = array();
43
+ foreach ($this->getAliases() as $index => $alias) {
44
+ $aliases[$index] = $prefix->getAlias() . '.' . $alias;
45
+ }
46
+
47
+ return $formulaHelper->createAlias($aliases);
48
+ }
49
+
50
+ /**
51
+ * @param Mana_Db_Model_Formula_Alias_Array $prefix
52
+ * @return Mana_Db_Model_Formula_Alias
53
+ */
54
+ public function arrayChild($prefix) {
55
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
56
+ $formulaHelper = Mage::helper('mana_db/formula');
57
+
58
+ $aliases = array();
59
+ foreach ($this->getAliases() as $thisIndex => $thisAlias) {
60
+ foreach ($prefix->getAliases() as $prefixIndex => $prefixAlias) {
61
+ $aliases["$prefixIndex.$thisIndex"] = $prefixAlias . '.' . $thisAlias;
62
+ }
63
+ }
64
+
65
+ return $formulaHelper->createAlias($aliases);
66
+ }
67
+
68
+ /**
69
+ * @param Mana_Db_Model_Formula_Closure $closure
70
+ * @return mixed
71
+ */
72
+ public function each($closure) {
73
+ $closure->setAlias($this);
74
+ $result = array();
75
+ foreach ($this->getAliases() as $index => $alias) {
76
+ $result[$index] = $closure
77
+ ->setIndex($index)
78
+ ->execute();
79
+ }
80
+ return $result;
81
+ }
82
+
83
+ /**
84
+ * @param mixed $index
85
+ * @return string
86
+ */
87
+ public function asString($index) {
88
+ return $this->_aliases[$index];
89
+ }
90
+
91
+ /**
92
+ * @param Mana_Db_Model_Formula_Context $context
93
+ * @param string $expr
94
+ * @return string | string[]
95
+ */
96
+ public function expr($context, $expr) {
97
+ $result = array();
98
+ foreach ($this->getAliases() as $index => $alias) {
99
+ $result[$index] = $context->resolveAliases($expr, true, $index);
100
+ }
101
+ return $result;
102
+ }
103
+
104
+ }
app/code/local/Mana/Db/Model/Formula/Alias/Empty.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Alias_Empty extends Mana_Db_Model_Formula_Alias {
13
+ /**
14
+ * @param Mana_Db_Model_Formula_Alias $suffix
15
+ * @return Mana_Db_Model_Formula_Alias
16
+ */
17
+ public function child($suffix) {
18
+ return $suffix;
19
+ }
20
+
21
+ /**
22
+ * @param Mana_Db_Model_Formula_Alias_Single $prefix
23
+ * @throws Exception
24
+ * @return Mana_Db_Model_Formula_Alias
25
+ */
26
+ public function singleChild($prefix) {
27
+ return $prefix;
28
+ }
29
+
30
+ /**
31
+ * @param Mana_Db_Model_Formula_Alias_Array $prefix
32
+ * @throws Exception
33
+ * @return Mana_Db_Model_Formula_Alias
34
+ */
35
+ public function arrayChild($prefix) {
36
+ return $prefix;
37
+ }
38
+
39
+ /**
40
+ * @param Mana_Db_Model_Formula_Closure $closure
41
+ * @return mixed
42
+ */
43
+ public function each($closure) {
44
+ return $closure
45
+ ->setIndex(null)
46
+ ->setAlias($this)
47
+ ->execute();
48
+ }
49
+
50
+ /**
51
+ * @param mixed $index
52
+ * @throws Exception
53
+ * @return string
54
+ */
55
+ public function asString($index) {
56
+ return 'primary';
57
+ }
58
+
59
+ /**
60
+ * @param Mana_Db_Model_Formula_Context $context
61
+ * @param string $expr
62
+ * @throws Exception
63
+ * @return string | string[]
64
+ */
65
+ public function expr($context, $expr) {
66
+ return $context->resolveAliases($expr);
67
+ }
68
+ }
app/code/local/Mana/Db/Model/Formula/Alias/Single.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Alias_Single extends Mana_Db_Model_Formula_Alias {
13
+ /**
14
+ * @var string
15
+ */
16
+ protected $_alias;
17
+ public function __construct($alias) {
18
+ $this->_alias = $alias;
19
+ }
20
+
21
+ public function getAlias() {
22
+ return $this->_alias;
23
+ }
24
+
25
+ /**
26
+ * @param Mana_Db_Model_Formula_Alias $suffix
27
+ * @return Mana_Db_Model_Formula_Alias
28
+ */
29
+ public function child($suffix) {
30
+ return $suffix->singleChild($this);
31
+ }
32
+
33
+ /**
34
+ * @param Mana_Db_Model_Formula_Alias_Single $prefix
35
+ * @return Mana_Db_Model_Formula_Alias
36
+ */
37
+ public function singleChild($prefix) {
38
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
39
+ $formulaHelper = Mage::helper('mana_db/formula');
40
+
41
+ return $formulaHelper->createAlias($prefix->getAlias().'.'.$this->getAlias());
42
+ }
43
+
44
+ /**
45
+ * @param Mana_Db_Model_Formula_Alias_Array $prefix
46
+ * @return Mana_Db_Model_Formula_Alias
47
+ */
48
+ public function arrayChild($prefix) {
49
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
50
+ $formulaHelper = Mage::helper('mana_db/formula');
51
+
52
+ $aliases = array();
53
+ foreach ($prefix->getAliases() as $index => $alias) {
54
+ $aliases[$index] = $alias . '.' . $this->getAlias();
55
+ }
56
+ return $formulaHelper->createAlias($aliases);
57
+ }
58
+
59
+ /**
60
+ * @param Mana_Db_Model_Formula_Closure $closure
61
+ * @return mixed
62
+ */
63
+ public function each($closure) {
64
+ return $closure
65
+ ->setIndex(null)
66
+ ->setAlias($this)
67
+ ->execute();
68
+ }
69
+
70
+ /**
71
+ * @param mixed $index
72
+ * @return string
73
+ */
74
+ public function asString($index) {
75
+ return $this->getAlias();
76
+ }
77
+
78
+ /**
79
+ * @param Mana_Db_Model_Formula_Context $context
80
+ * @param string $expr
81
+ * @return string | string[]
82
+ */
83
+ public function expr($context, $expr) {
84
+ return $context->resolveAliases($expr);
85
+ }
86
+ }
app/code/local/Mana/Db/Model/Formula/Closure.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method mixed getIndex()
11
+ * @method Mana_Db_Model_Formula_Closure setIndex(mixed $value)
12
+ * @method Mana_Db_Model_Formula_Alias getAlias()
13
+ * @method Mana_Db_Model_Formula_Closure setAlias(Mana_Db_Model_Formula_Alias $value)
14
+ */
15
+ class Mana_Db_Model_Formula_Closure extends Mana_Core_Model_Closure {
16
+ }
app/code/local/Mana/Db/Model/Formula/Closure/AggregateFieldJoin.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method Mana_Db_Model_Formula_Context getContext()
11
+ * @method Mana_Db_Model_Formula_Closure_AggregateFieldJoin setContext(Mana_Db_Model_Formula_Context $value)
12
+ * @method int getTargetIndex()
13
+ * @method Mana_Db_Model_Formula_Closure_AggregateFieldJoin setTargetIndex(int $value)
14
+ * @method Mana_Db_Model_Formula_Entity getResult()
15
+ * @method Mana_Db_Model_Formula_Closure_AggregateFieldJoin setResult(Mana_Db_Model_Formula_Entity $value)
16
+ * @method array getCompositeAlias()
17
+ * @method Mana_Db_Model_Formula_Closure_AggregateFieldJoin setCompositeAlias(array $value)
18
+ * @method array getFields()
19
+ * @method Mana_Db_Model_Formula_Closure_AggregateFieldJoin setFields(array $value)
20
+ * @method string getEntityName()
21
+ * @method Mana_Db_Model_Formula_Closure_AggregateFieldJoin setEntityName(string $value)
22
+ * @method Varien_Simplexml_Element getFieldXml()
23
+ * @method Mana_Db_Model_Formula_Closure_AggregateFieldJoin setFieldXml(Varien_Simplexml_Element $value)
24
+ *
25
+ */
26
+ class Mana_Db_Model_Formula_Closure_AggregateFieldJoin extends Mana_Db_Model_Formula_Closure {
27
+ /**
28
+ * @return mixed|void
29
+ */
30
+ public function execute() {
31
+ $context = $this->getContext();
32
+ $fieldXml = $this->getFieldXml();
33
+
34
+ $fieldAlias = $this->getAlias()->asString($this->getIndex());
35
+
36
+ $compositeAlias = $this->getCompositeAlias() ? $this->getCompositeAlias() : array();
37
+ $compositeAlias[$this->getTargetIndex()] = $fieldAlias;
38
+ $this->setCompositeAlias($compositeAlias);
39
+
40
+ $fields = $this->getFields() ? $this->getFields() : array();
41
+ /** @noinspection PhpUndefinedFieldInspection */
42
+ $fields[$this->getTargetIndex()] = array(
43
+ 'alias' => $fieldAlias,
44
+ 'entity' => (string)$fieldXml->foreign->entity,
45
+ 'join' => "{{= {$fieldAlias}.{$this->getResult()->getProcessor()->getPrimaryKey($this->getEntityName())} }} = " .
46
+ "{{= {$context->getAlias()->asString($this->getIndex())}.{$fieldXml->getName()} }}",
47
+ );
48
+ $this->setFields($fields);
49
+ }
50
+ }
app/code/local/Mana/Db/Model/Formula/Closure/ForeignJoin.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method Mana_Db_Model_Formula_Context getContext()
11
+ * @method Mana_Db_Model_Formula_Closure_ForeignJoin setContext(Mana_Db_Model_Formula_Context $value)
12
+ * @method Mana_Db_Model_Formula_Entity getEntity()
13
+ * @method Mana_Db_Model_Formula_Closure_ForeignJoin setEntity(Mana_Db_Model_Formula_Entity $value)
14
+ */
15
+ class Mana_Db_Model_Formula_Closure_ForeignJoin extends Mana_Db_Model_Formula_Closure {
16
+ /**
17
+ * @return mixed|void
18
+ */
19
+ public function execute() {
20
+ /* @var $dbConfig Mana_Db_Helper_Config */
21
+ $dbConfig = Mage::helper('mana_db/config');
22
+
23
+ $entity = $this->getEntity();
24
+ $context = $this->getContext();
25
+ return "{{= {$entity->getAlias()->asString($this->getIndex())}.{$entity->getProcessor()->getPrimaryKey($entity->getEntity())} }} = " .
26
+ "{{= {$context->getAlias()->asString($this->getIndex())}.{$dbConfig->getForeignKey($entity->getEntity(), $context->getEntity())} }}";
27
+ }
28
+ }
app/code/local/Mana/Db/Model/Formula/Closure/ForeignJoinEnd.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method Mana_Db_Model_Formula_Context getContext()
11
+ * @method Mana_Db_Model_Formula_Closure_ForeignJoin setContext(Mana_Db_Model_Formula_Context $value)
12
+ * @method Mana_Db_Model_Formula_Entity getEntity()
13
+ * @method Mana_Db_Model_Formula_Closure_ForeignJoin setEntity(Mana_Db_Model_Formula_Entity $value)
14
+ */
15
+ class Mana_Db_Model_Formula_Closure_ForeignJoinEnd extends Mana_Db_Model_Formula_Closure {
16
+ /**
17
+ * @return mixed|void
18
+ */
19
+ public function execute() {
20
+ /* @var $resource Mana_Db_Resource_Formula */
21
+ $resource = Mage::getResourceSingleton('mana_db/formula');
22
+
23
+ /* @var $dbHelper Mana_Db_Helper_Data */
24
+ $dbHelper = Mage::helper('mana_db');
25
+
26
+ $entity = $this->getEntity();
27
+ $context = $this->getContext();
28
+
29
+ $context->getSelect()->joinLeft(
30
+ array(
31
+ $context->registerAlias($entity->getAlias()->asString($this->getIndex())) =>
32
+ $resource->getTable($dbHelper->getScopedName($entity->getEntity()))
33
+ ),
34
+ $context->resolveAliases($entity->getForeignJoin()),
35
+ null
36
+ );
37
+ }
38
+ }
app/code/local/Mana/Db/Model/Formula/Closure/Join.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method Mana_Db_Model_Formula_Context getContext()
11
+ * @method Mana_Db_Model_Formula_Closure_Join setContext(Mana_Db_Model_Formula_Context $value)
12
+ * @method Varien_Simplexml_Element getDefinition()
13
+ * @method Mana_Db_Model_Formula_Closure_Join setDefinitionVarien_Simplexml_Element $value)
14
+ */
15
+ class Mana_Db_Model_Formula_Closure_Join extends Mana_Db_Model_Formula_Closure {
16
+ /**
17
+ * @return mixed|void
18
+ */
19
+ public function execute() {
20
+ /* @var $dbHelper Mana_Db_Helper_Data */
21
+ $dbHelper = Mage::helper('mana_db');
22
+
23
+ /* @var $resource Mana_Db_Resource_Formula */
24
+ $resource = Mage::getResourceSingleton('mana_db/formula');
25
+
26
+ $definition = $this->getDefinition();
27
+ $alias = $definition->getName();
28
+ $fullAlias = $this->getAlias()->asString($this->getIndex());
29
+
30
+ $this->getContext()->addLocalAlias($alias, $this->getAlias());
31
+ if (!$this->getContext()->hasAlias($fullAlias)) {
32
+ /** @noinspection PhpUndefinedFieldInspection */
33
+ $entity = $alias == 'primary' ? $this->getContext()->getPrimaryEntity() : (string)$definition->entity;
34
+ $method = isset($definition->type) ? 'join' . ucfirst($definition->type) : 'joinInner';
35
+
36
+ /** @noinspection PhpUndefinedFieldInspection */
37
+ $this->getContext()->getSelect()->$method(
38
+ array($this->getContext()->registerAlias($fullAlias) => $resource->getTable($dbHelper->getScopedName($entity))),
39
+ $this->getContext()->resolveAliases((string)$definition->on, true, $this->getIndex()),
40
+ null
41
+ );
42
+ }
43
+ }
44
+ }
app/code/local/Mana/Db/Model/Formula/Context.php ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method string getMode()
11
+ * @method Mana_Db_Model_Formula_Context setMode(string $value)
12
+ * @method string getPrefix()
13
+ * @method Mana_Db_Model_Formula_Context setPrefix(string $value)
14
+ * @method string getEntity()
15
+ * @method Mana_Db_Model_Formula_Context setEntity(string $value)
16
+ * @method string getTargetEntity()
17
+ * @method Mana_Db_Model_Formula_Context setTargetEntity(string $value)
18
+ * @method string getPrimaryEntity()
19
+ * @method Mana_Db_Model_Formula_Context setPrimaryEntity(string $value)
20
+ * @method Mage_Core_Model_Abstract getModel()
21
+ * @method Mana_Db_Model_Formula_Context setModel(Mage_Core_Model_Abstract $value)
22
+ * @method Mana_Db_Helper_Formula_Processor getProcessor()
23
+ * @method mixed getResult()
24
+ * @method Mana_Db_Model_Formula_Context setResult(mixed $value)
25
+ * @method Mana_Db_Model_Formula_Field getField()
26
+ * @method Mana_Db_Model_Formula_Context setField(Mana_Db_Model_Formula_Field $value)
27
+ * @method Mana_Db_Helper_Formula_Abstract getHelper()
28
+ * @method Mana_Db_Model_Formula_Context setHelper(Mana_Db_Helper_Formula_Abstract $value)
29
+ * @method bool getIsAggregate()
30
+ * @method Mana_Db_Model_Formula_Context setIsAggregate(bool $value)
31
+ * @method Mana_Db_Model_Formula_Context getParentContext()
32
+ * @method Mana_Db_Model_Formula_Context setParentContext(Mana_Db_Model_Formula_Context $value)
33
+ * @method Mana_Db_Model_Formula_Context getAggregateContext()
34
+ * @method Mana_Db_Model_Formula_Context setAggregateContext(Mana_Db_Model_Formula_Context $value)
35
+ * @method Mana_Db_Model_Formula_Alias getAlias()
36
+ * @method Mana_Db_Model_Formula_Context setAlias(Mana_Db_Model_Formula_Alias $value)
37
+ * @method string __getGlobalEntity()
38
+ * @method Mana_Db_Model_Formula_Context __setGlobalEntity(string $value)
39
+ * @method Mana_Db_Helper_Formula_Entity getEntityHelper()
40
+ * @method Mana_Db_Model_Formula_Context setEntityHelper(Mana_Db_Helper_Formula_Entity $value)
41
+ */
42
+ class Mana_Db_Model_Formula_Context extends Varien_Object {
43
+ /**
44
+ * @var Varien_Db_Select()
45
+ */
46
+ protected $_select;
47
+ /**
48
+ * @var string[]
49
+ */
50
+ protected $_fields = array();
51
+ protected $_aliases = array();
52
+ protected $_localAliases = array();
53
+
54
+ protected $_options = array();
55
+
56
+ static protected $_quoteFieldsAndEntities = true;
57
+ static protected $_aliasIndex = null;
58
+
59
+ /**
60
+ * @param Mana_Db_Helper_Formula_Processor | string $processor
61
+ * @return Mana_Db_Model_Formula_Context
62
+ */
63
+ public function setProcessor($processor) {
64
+ if (is_string($processor)) {
65
+ $processor = Mage::helper('mana_db/formula_processor_' . $processor);
66
+ }
67
+ $this->setData('processor', $processor);
68
+
69
+ return $this;
70
+ }
71
+
72
+ /**
73
+ * @return Mana_Db_Model_Formula_Context
74
+ */
75
+ public function createChildContext() {
76
+ /* @var $result Mana_Db_Model_Formula_Context */
77
+ $result = Mage::getModel('mana_db/formula_context');
78
+ return $result
79
+ ->setHelper($this->getHelper())
80
+ ->setParentContext($this);
81
+ }
82
+
83
+ /**
84
+ * @param Mana_Db_Model_Formula_Context $context
85
+ * @return Mana_Db_Model_Formula_Context
86
+ */
87
+ public function copyAliases($context) {
88
+ $this->_aliases = $context->_aliases;
89
+ return $this;
90
+ }
91
+
92
+ public function registerAlias($alias) {
93
+ if (!isset($this->_aliases[$alias])) {
94
+ $relativeAlias = explode('.', $alias);
95
+ $relativeAlias = array_pop($relativeAlias);
96
+ $letter = $this->getPrefix().substr($relativeAlias, 0, 1);
97
+ for ($i = 1, $result = $letter; in_array($result, $this->_aliases); $i++, $result = $letter . $i) ;
98
+ $this->_aliases[$alias] = $result;
99
+ }
100
+ return $this->_aliases[$alias];
101
+ }
102
+
103
+ public function resolveAliases($expr, $quoteFieldsAndEntities = true, $aliasIndex = null) {
104
+ self::$_quoteFieldsAndEntities = $quoteFieldsAndEntities;
105
+ self::$_aliasIndex = $aliasIndex;
106
+ return preg_replace_callback('/{{=([^}]*)}}/', array($this, '_resolveAlias'), $expr);
107
+ }
108
+
109
+ public function hasAlias($alias) {
110
+ return isset($this->_aliases[$alias]);
111
+ }
112
+
113
+
114
+ public function resetLocalAliases() {
115
+ $this->_localAliases = array();
116
+
117
+ return $this;
118
+ }
119
+
120
+ public function addLocalAlias($localAlias, $globalAlias) {
121
+ $this->_localAliases[$localAlias] = $globalAlias;
122
+ return $this;
123
+ }
124
+
125
+ protected function _resolveAlias($matches) {
126
+ return $this->_doResolveAlias($matches[1]);
127
+ }
128
+
129
+ public function resolveAlias($fieldExpr, $quoteFieldsAndEntities = true, $aliasIndex = null) {
130
+ self::$_quoteFieldsAndEntities = $quoteFieldsAndEntities;
131
+ self::$_aliasIndex = $aliasIndex;
132
+ return $this->_doResolveAlias($fieldExpr);
133
+ }
134
+ protected function _doResolveAlias($fieldExpr) {
135
+ $fieldExpr = explode('.', $fieldExpr);
136
+ foreach ($fieldExpr as $index => $field) {
137
+ $fieldExpr[$index] = trim($field);
138
+ }
139
+ if ($fieldExpr[0] == 'parent') {
140
+ return $this->getParentContext()->_doResolveAlias(implode('.', array_slice($fieldExpr, 1)));
141
+ }
142
+ else {
143
+ $field = array_pop($fieldExpr);
144
+ $alias = implode('.', $fieldExpr);
145
+ if (!$alias) {
146
+ $alias = 'primary';
147
+ }
148
+
149
+ if ($fieldExpr[0] == 'context') {
150
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
151
+ $formulaHelper = Mage::helper('mana_db/formula');
152
+
153
+ $alias = $this->getAlias()->child($formulaHelper->createAlias(implode('.', array_slice($fieldExpr, 1))));
154
+ }
155
+ elseif (isset($this->_localAliases[$alias])) {
156
+ $alias = $this->_localAliases[$alias];
157
+ }
158
+
159
+ if (!is_string($alias)) {
160
+ $alias = $alias->asString(self::$_aliasIndex);
161
+ }
162
+
163
+ if (self::$_quoteFieldsAndEntities) {
164
+ return "`{$this->registerAlias($alias)}`.`$field`";
165
+ }
166
+ else {
167
+ return "{$this->registerAlias($alias)}.$field";
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * @return Varien_Db_Select
174
+ */
175
+ public function getSelect() {
176
+ if (!$this->_select) {
177
+ /* @var $resource Mana_Db_Resource_Formula */
178
+ $resource = Mage::getResourceSingleton('mana_db/formula');
179
+
180
+ $this->_select = $resource->select();
181
+ }
182
+ return $this->_select;
183
+ }
184
+
185
+ public function incrementPrefix() {
186
+ $alias = $this->getAlias() ? $this->getAlias() : 'primary';
187
+ $prefix = $this->getPrefix() . $this->registerAlias($alias);
188
+ $this->setPrefix($prefix);
189
+ return $prefix;
190
+ }
191
+
192
+ public function decrementPrefix() {
193
+ $alias = $this->getAlias() ? $this->getAlias() : 'primary';
194
+ $prefix = substr($this->getPrefix(), 0, strlen($this->getPrefix()) - strlen($this->registerAlias($alias)));
195
+ $this->setPrefix($prefix);
196
+
197
+ return $prefix;
198
+ }
199
+
200
+ public function getFields() {
201
+ return $this->_fields;
202
+ }
203
+
204
+ public function addField($field) {
205
+ $this->_fields[] = $field;
206
+ return $this;
207
+ }
208
+
209
+ public function setOptions($options) {
210
+ $this->_options = $options;
211
+ return $this;
212
+ }
213
+
214
+ public function getOption($option) {
215
+ return isset($this->_options[$option]) ? $this->_options[$option] : false;
216
+ }
217
+ }
app/code/local/Mana/Db/Model/Formula/Entity.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method Mana_Db_Helper_Formula_Entity getHelper()
11
+ * @method Mana_Db_Model_Formula_Alias getAlias()
12
+ * @method Mana_Db_Model_Formula_Entity setAlias(Mana_Db_Model_Formula_Alias $value)
13
+ * @method string getForeignJoin()
14
+ * @method Mana_Db_Model_Formula_Entity setForeignJoin(string $value)
15
+ * @method string getEntity()
16
+ * @method Mana_Db_Model_Formula_Entity setEntity(string $value)
17
+ * @method string[] | bool getJoin()
18
+ * @method Mana_Db_Model_Formula_Entity setJoin(array $value)
19
+ * @method string getOrder()
20
+ * @method Mana_Db_Model_Formula_Entity setOrder(string $value)
21
+ * @method string getWhere()
22
+ * @method Mana_Db_Model_Formula_Entity setWhere(string $value)
23
+ * @method Mana_Db_Helper_Formula_Processor getProcessor()
24
+ * @method string[] | bool getAggregateFields()
25
+ * @method Mana_Db_Model_Formula_Entity setAggregateFields(array $value)
26
+ */
27
+ class Mana_Db_Model_Formula_Entity extends Varien_Object {
28
+ /**
29
+ * @param Mana_Db_Helper_Formula_Entity | string $entity
30
+ * @return Mana_Db_Model_Formula_Entity
31
+ */
32
+ public function setHelper($entity) {
33
+ if (is_string($entity)) {
34
+ $entity = Mage::helper('mana_db/formula_entity_' . $entity);
35
+ }
36
+ $this->setData('helper', $entity);
37
+
38
+ return $this;
39
+ }
40
+
41
+ /**
42
+ * @param Mana_Db_Helper_Formula_Processor | string $processor
43
+ * @return Mana_Db_Model_Formula_Entity
44
+ */
45
+ public function setProcessor($processor) {
46
+ if (is_string($processor)) {
47
+ $processor = Mage::helper('mana_db/formula_processor_' . $processor);
48
+ }
49
+ $this->setData('processor', $processor);
50
+
51
+ return $this;
52
+ }
53
+ }
app/code/local/Mana/Db/Model/Formula/Expr.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method string getType()
11
+ * @method Mana_Db_Model_Formula_Expr setType(string $value)
12
+ * @method string getExpr()
13
+ * @method Mana_Db_Model_Formula_Expr setExpr(string $value)
14
+ * @method string | string[] getFieldExpr()
15
+ * @method Mana_Db_Model_Formula_Expr setFieldExpr(mixed $value)
16
+ * @method string getFieldName()
17
+ * @method Mana_Db_Model_Formula_Expr setFieldName(string $value)
18
+ * @method Varien_Db_Select getSubSelect()
19
+ * @method Mana_Db_Model_Formula_Expr setSubSelect(Varien_Db_Select $value)
20
+ * @method bool getIsAggregate()
21
+ * @method Mana_Db_Model_Formula_Expr setIsAggregate(bool $value)
22
+ * @method bool getIsFrontend()
23
+ * @method Mana_Db_Model_Formula_Expr setIsFrontend(bool $value)
24
+ */
25
+ class Mana_Db_Model_Formula_Expr extends Varien_Object {
26
+ }
app/code/local/Mana/Db/Model/Formula/Field.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method string getName()
11
+ * @method Mana_Db_Model_Formula_Field setName(string $value)
12
+ * @method string getType()
13
+ * @method Mana_Db_Model_Formula_Field setType(string $value)
14
+ * @method string getRole()
15
+ * @method Mana_Db_Model_Formula_Field setRole(string $value)
16
+ * @method int getNo()
17
+ * @method Mana_Db_Model_Formula_Field setNo(int $value)
18
+ * @method bool hasFormula()
19
+ * @method Mana_Db_Model_Formula_Node getFormula()
20
+ * @method Mana_Db_Model_Formula_Field setFormula(Mana_Db_Model_Formula_Node $value)
21
+ * @method bool hasValue()
22
+ * @method string getValue()
23
+ * @method Mana_Db_Model_Formula_Field setValue(string $value)
24
+ * @method bool hasDependencies()
25
+ * @method string[] getDependencies()
26
+ * @method Mana_Db_Model_Formula_Field setDependencies(array $value)
27
+ * @method string getFormulaString()
28
+ * @method Mana_Db_Model_Formula_Field setFormulaString(string $value)
29
+ */
30
+ class Mana_Db_Model_Formula_Field extends Varien_Object {
31
+ /**
32
+ * @param Mana_Db_Model_Entity $model
33
+ * @return bool
34
+ */
35
+ public function useDefault($model) {
36
+ }
37
+ }
app/code/local/Mana/Db/Model/Formula/Node.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ */
11
+ class Mana_Db_Model_Formula_Node {
12
+ public function __construct($data = array()) {
13
+ foreach ($data as $property => $value) {
14
+ $this->$property = $value;
15
+ }
16
+ }
17
+ }
app/code/local/Mana/Db/Model/Formula/Node/Add.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_Add extends Mana_Db_Model_Formula_Node {
13
+ const ADD = Mana_Db_Helper_Formula_Parser::ADD;
14
+ const SUBTRACT = Mana_Db_Helper_Formula_Parser::SUBTRACT;
15
+
16
+ /**
17
+ * @var int
18
+ */
19
+ public $operator;
20
+ /**
21
+ * @var Mana_Db_Model_Formula_Node
22
+ */
23
+ public $a;
24
+ /**
25
+ * @var Mana_Db_Model_Formula_Node
26
+ */
27
+ public $b;
28
+ }
app/code/local/Mana/Db/Model/Formula/Node/Field.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_Field extends Mana_Db_Model_Formula_Node {
13
+ /**
14
+ * @var string[]
15
+ */
16
+ public $identifiers;
17
+
18
+ public function __toString() {
19
+ return implode('.', $this->identifiers);
20
+ }
21
+ }
app/code/local/Mana/Db/Model/Formula/Node/FormulaExpr.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_FormulaExpr extends Mana_Db_Model_Formula_Node {
13
+ /**
14
+ * @var Mana_Db_Model_Formula_Node[]
15
+ */
16
+ public $parts;
17
+ }
app/code/local/Mana/Db/Model/Formula/Node/FunctionCall.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_FunctionCall extends Mana_Db_Model_Formula_Node {
13
+ /**
14
+ * @var string
15
+ */
16
+ public $name;
17
+ /**
18
+ * @var Mana_Db_Model_Formula_Node[]
19
+ */
20
+ public $args;
21
+ }
app/code/local/Mana/Db/Model/Formula/Node/Identifier.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_Identifier extends Mana_Db_Model_Formula_Node {
13
+ /**
14
+ * @var string
15
+ */
16
+ public $identifier;
17
+ }
app/code/local/Mana/Db/Model/Formula/Node/Multiply.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_Multiply extends Mana_Db_Model_Formula_Node {
13
+ const MULTIPLY = Mana_Db_Helper_Formula_Parser::MULTIPLY;
14
+ const DIVIDE = Mana_Db_Helper_Formula_Parser::DIVIDE;
15
+
16
+ /**
17
+ * @var int
18
+ */
19
+ public $operator;
20
+ /**
21
+ * @var Mana_Db_Model_Formula_Node
22
+ */
23
+ public $a;
24
+ /**
25
+ * @var Mana_Db_Model_Formula_Node
26
+ */
27
+ public $b;
28
+ }
app/code/local/Mana/Db/Model/Formula/Node/NullValue.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_NullValue extends Mana_Db_Model_Formula_Node {
13
+ }
app/code/local/Mana/Db/Model/Formula/Node/NumberConstant.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_NumberConstant extends Mana_Db_Model_Formula_Node {
13
+ /**
14
+ * @var string
15
+ */
16
+ public $value;
17
+ }
app/code/local/Mana/Db/Model/Formula/Node/StringConstant.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Formula_Node_StringConstant extends Mana_Db_Model_Formula_Node {
13
+ /**
14
+ * @var string
15
+ */
16
+ public $value;
17
+ }
app/code/local/Mana/Db/Model/Indexer.php CHANGED
@@ -20,7 +20,7 @@ class Mana_Db_Model_Indexer extends Mage_Index_Model_Indexer_Abstract {
20
  }
21
  public function getName()
22
  {
23
- return Mage::helper('mana_db')->__('Default Values');
24
  }
25
  public function getDescription()
26
  {
20
  }
21
  public function getName()
22
  {
23
+ return Mage::helper('mana_db')->__('Default Values (MANAdev)');
24
  }
25
  public function getDescription()
26
  {
app/code/local/Mana/Db/Model/Observer.php CHANGED
@@ -28,7 +28,19 @@ class Mana_Db_Model_Observer {
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
@@ -101,13 +113,28 @@ class Mana_Db_Model_Observer {
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
  }
28
  Mage::unregister('m_run_db_replication');
29
  $this->_getProcess()->changeStatus(Mage_Index_Model_Process::STATUS_REQUIRE_REINDEX)->reindexAll();
30
  }
31
+
32
+ /* @var $indexer Mage_Index_Model_Indexer */
33
+ $indexer = Mage::getSingleton('index/indexer');
34
+
35
+ if ($reindex = Mage::registry('m_reindex')) {
36
+ foreach ($reindex as $code) {
37
+ $indexer->getProcessByCode($code)
38
+ ->changeStatus(Mage_Index_Model_Process::STATUS_REQUIRE_REINDEX)
39
+ ->reindexAll();
40
+ }
41
+ }
42
+
43
+ }
44
  /**
45
  * Enter description here ...
46
  * @param unknown_type $observer
113
  * Runs data replication actions if related configuration changed (handles event "core_config_data_save_commit_after")
114
  * @param Varien_Event_Observer $observer
115
  */
116
+ public function afterSaveCommit($observer) {
117
+ $object = $observer->getEvent()->getObject();
118
+ if ($object->getResourceName() == 'core/config_data' &&
119
+ Mage::helper('mana_db')->isReplicatedConfigChanged($object))
120
  {
121
  Mage::register('m_run_db_replication', true);
122
  }
123
+ elseif ($object->getResourceName() == 'cms/page') {
124
+ if (!Mage::registry('m_prevent_indexing_on_save')) {
125
+ $this->getIndexerSingleton()->processEntityAction($object, 'cms/page', Mage_Index_Model_Event::TYPE_SAVE);
126
+ }
127
+ }
128
  }
129
+
130
+ public function afterConfigSave($observer) {}
131
+
132
+ #region Dependencies
133
+ /**
134
+ * @return Mage_Index_Model_Indexer
135
+ */
136
+ public function getIndexerSingleton() {
137
+ return Mage::getSingleton('index/indexer');
138
+ }
139
+ #endregion
140
  }
app/code/local/Mana/Db/Model/Setup.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method Mage_Core_Model_Resource_Setup getInstaller()
11
+ * @method Mana_Db_Model_Setup setInstaller(Mage_Core_Model_Resource_Setup $value)
12
+ * @method string getModuleName()
13
+ * @method Mana_Db_Model_Setup setModuleName(string $value)
14
+ * @method string getVersion()
15
+ * @method Mana_Db_Model_Setup setVersion(string $value)
16
+ * @method string getSetupVersion()
17
+ * @method Mana_Db_Model_Setup setSetupVersion(string $value)
18
+ */
19
+ class Mana_Db_Model_Setup extends Varien_Object {
20
+ public function run ($installer, $moduleName, $version) {
21
+ /* @var $configHelper Mana_Db_Helper_Config */
22
+ $configHelper = Mage::helper('mana_db/config');
23
+
24
+ $installerKey = 'v'.$version;
25
+ $setupVersion = $configHelper->getXml()->getNode()->modules->$moduleName->installer_versions->$installerKey;
26
+
27
+ $this
28
+ ->setInstaller($installer)
29
+ ->setModuleName($moduleName)
30
+ ->setVersion($version)
31
+ ->setSetupVersion($setupVersion)
32
+ ->_beforeRun()
33
+ ->_run()
34
+ ->_afterRun();
35
+ }
36
+ protected function _beforeRun() {
37
+ $installer = $this->getInstaller();
38
+
39
+ if (defined('COMPILER_INCLUDE_PATH')) {
40
+ throw new Exception(Mage::helper('mana_core')->__(
41
+ '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.'
42
+ ));
43
+ }
44
+
45
+ if (method_exists($installer->getConnection(), 'allowDdlCache')) {
46
+ $installer->getConnection()->allowDdlCache();
47
+ }
48
+
49
+ return $this;
50
+ }
51
+ protected function _run() {
52
+ /* @var $configHelper Mana_Db_Helper_Config */
53
+ $configHelper = Mage::helper('mana_db/config');
54
+
55
+ $setupVersion = $configHelper->getSetup($this->getSetupVersion());
56
+ $setupVersion->setData($this->getData());
57
+ $setupVersion->run();
58
+
59
+ return $this;
60
+ }
61
+ protected function _afterRun() {
62
+ $installer = $this->getInstaller();
63
+
64
+ if (method_exists($installer->getConnection(), 'disallowDdlCache')) {
65
+ $installer->getConnection()->disallowDdlCache();
66
+ }
67
+ $installer->endSetup();
68
+
69
+ return $this;
70
+ }
71
+
72
+ public function scheduleReindexing($code) {
73
+ if ($reindex = Mage::registry('m_reindex')) {
74
+ Mage::unregister('m_reindex');
75
+ }
76
+ else {
77
+ $reindex = array();
78
+ }
79
+ $reindex[$code] = $code;
80
+ Mage::register('m_reindex', $reindex);
81
+
82
+ return $this;
83
+ }
84
+ }
app/code/local/Mana/Db/Model/Setup/Abstract.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method Mage_Core_Model_Resource_Setup getInstaller()
11
+ * @method Mana_Db_Model_Setup_Abstract setInstaller(Mage_Core_Model_Resource_Setup $value)
12
+ * @method string getModuleName()
13
+ * @method Mana_Db_Model_Setup_Abstract setModuleName(string $value)
14
+ * @method string getVersion()
15
+ * @method Mana_Db_Model_Setup_Abstract setVersion(string $value)
16
+ * @method string getSql()
17
+ * @method Mana_Db_Model_Setup_Abstract setSql(string $value)
18
+ */
19
+ class Mana_Db_Model_Setup_Abstract extends Varien_Object {
20
+ public function getTable($entityName) {
21
+ /* @var $db Mana_Db_Helper_Data */
22
+ $db = Mage::helper('mana_db');
23
+
24
+ return $this->getInstaller()->getTable($db->getScopedName($entityName));
25
+ }
26
+ public function prepare() {
27
+ return $this;
28
+ }
29
+
30
+ public function run() {
31
+ return $this;
32
+ }
33
+ }
app/code/local/Mana/Db/Model/Setup/V13012122.php ADDED
@@ -0,0 +1,478 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Model_Setup_V13012122 extends Mana_Db_Model_Setup_Abstract {
13
+ protected $_toBeCreated = array();
14
+ protected $_toBeAltered = array();
15
+
16
+ public function prepare() {
17
+ /* @var $configHelper Mana_Db_Helper_Config */
18
+ $configHelper = Mage::helper('mana_db/config');
19
+
20
+ $configHelper->iterate(array('scope' => array($this, '_createPrimaryKey')));
21
+ $configHelper->iterate(array('scope' => array($this, '_createDefaultValueInfrastructure')));
22
+ $configHelper->iterate(array('scope' => array($this, '_createStoreSpecificsInfrastructure')));
23
+ $configHelper->iterate(array('scope' => array($this, '_createGridEditingInfrastructure')));
24
+ $configHelper->iterate(array('scope' => array($this, '_createFlatInfrastructure')));
25
+
26
+ return $this;
27
+ }
28
+
29
+ public function run() {
30
+ /* @var $configHelper Mana_Db_Helper_Config */
31
+ $configHelper = Mage::helper('mana_db/config');
32
+
33
+ $result = $configHelper->iterate(array(
34
+ 'scope_before' => array($this, '_beginTableScript'),
35
+ 'field' => array($this, '_fieldScript'),
36
+ 'scope_after' => array($this, '_endTableScript'),
37
+ ));
38
+
39
+ if ($result->getSql()) {
40
+ $this->getInstaller()->run($result->getSql());
41
+ }
42
+ return $this;
43
+ }
44
+
45
+ /**
46
+ * @param Varien_Object $context
47
+ * @param Varien_Simplexml_Element $module
48
+ * @param Varien_Simplexml_Element $entity
49
+ * @param Varien_Simplexml_Element $scope
50
+ */
51
+ public function _createPrimaryKey($context, $module, $entity, $scope) {
52
+ if (((string)$scope['module']) != $this->getModuleName() || ((string)$scope['version']) != $this->getVersion()) {
53
+ return;
54
+ }
55
+ /* @var $configHelper Mana_Db_Helper_Config */
56
+ $configHelper = Mage::helper('mana_db/config');
57
+
58
+ $primaryField = $scope->xpath('fields/*[primary="1"]');
59
+ if (empty($primaryField)) {
60
+ $scope->fields->id->type = 'bigint';
61
+ $field = $scope->fields->id;
62
+ $field->primary = 1;
63
+ $field->role = Mana_Db_Helper_Config::ROLE_PRIMARY_KEY;
64
+ $configHelper->propagateName($field);
65
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @param Varien_Object $context
71
+ * @param Varien_Simplexml_Element $module
72
+ * @param Varien_Simplexml_Element $entity
73
+ * @param Varien_Simplexml_Element $scope
74
+ */
75
+ public function _createDefaultValueInfrastructure($context, $module, $entity, $scope) {
76
+ if (((string)$scope['module']) != $this->getModuleName() || ((string)$scope['version']) != $this->getVersion()) {
77
+ return;
78
+ }
79
+
80
+ /* @var $configHelper Mana_Db_Helper_Config */
81
+ $configHelper = Mage::helper('mana_db/config');
82
+
83
+ if (!empty($scope->max_defaultable_fields) && empty($scope->flattens)) {
84
+ $maxFields = (int)(string)$scope->max_defaultable_fields;
85
+ for ($i = 0; $i * 15 < $maxFields; $i++) {
86
+ $fieldName = "default_mask{$i}";
87
+ $scope->fields->$fieldName->type = 'int unsigned';
88
+ $field = $scope->fields->$fieldName;
89
+ $field->default_value = 0;
90
+ $field->role = Mana_Db_Helper_Config::ROLE_DEFAULT_VALUE;
91
+ $configHelper->propagateName($field);
92
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
93
+ }
94
+
95
+ $fieldName = 'default_formula_hash';
96
+ $scope->fields->$fieldName->type = 'varchar(40)';
97
+ $field = $scope->fields->$fieldName;
98
+ $field->indexed = 1;
99
+ $field->role = Mana_Db_Helper_Config::ROLE_DEFAULT_VALUE;
100
+ $configHelper->propagateName($field);
101
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
102
+
103
+
104
+ $fieldName = 'default_formulas';
105
+ $scope->fields->$fieldName->type = 'mediumtext';
106
+ $field = $scope->fields->$fieldName;
107
+ $field->role = Mana_Db_Helper_Config::ROLE_DEFAULT_VALUE;
108
+ $configHelper->propagateName($field);
109
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
110
+ }
111
+ }
112
+
113
+ /**
114
+ * @param Varien_Object $context
115
+ * @param Varien_Simplexml_Element $module
116
+ * @param Varien_Simplexml_Element $entity
117
+ * @param Varien_Simplexml_Element $scope
118
+ */
119
+ public function _createStoreSpecificsInfrastructure($context, $module, $entity, $scope) {
120
+ if (((string)$scope['module']) != $this->getModuleName() || ((string)$scope['version']) != $this->getVersion()) {
121
+ return;
122
+ }
123
+
124
+ /* @var $configHelper Mana_Db_Helper_Config */
125
+ $configHelper = Mage::helper('mana_db/config');
126
+
127
+ if (!empty($scope->store_specifics_for)) {
128
+ $globalScope = (string)$scope->store_specifics_for;
129
+
130
+ $fieldName = 'global_id';
131
+ $scope->fields->$fieldName->type = 'bigint';
132
+ $field = $scope->fields->$fieldName;
133
+ $field->foreign->entity = $globalScope;
134
+ $field->foreign->field = 'id';
135
+ $field->foreign->on_update = 'cascade';
136
+ $field->foreign->on_delete = 'cascade';
137
+ $field->role = Mana_Db_Helper_Config::ROLE_STORE_SPECIFICS;
138
+ $configHelper->propagateName($field);
139
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
140
+
141
+ $fieldName = 'store_id';
142
+ $scope->fields->$fieldName->type = 'smallint(5) unsigned';
143
+ $field = $scope->fields->$fieldName;
144
+ $field->foreign->entity = 'core/store';
145
+ $field->foreign->field = 'store_id';
146
+ $field->foreign->on_update = 'cascade';
147
+ $field->foreign->on_delete = 'cascade';
148
+ $field->role = Mana_Db_Helper_Config::ROLE_STORE_SPECIFICS;
149
+ $configHelper->propagateName($field);
150
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
151
+
152
+
153
+ }
154
+ }
155
+
156
+ /**
157
+ * @param Varien_Object $context
158
+ * @param Varien_Simplexml_Element $module
159
+ * @param Varien_Simplexml_Element $entity
160
+ * @param Varien_Simplexml_Element $scope
161
+ */
162
+ public function _createGridEditingInfrastructure($context, $module, $entity, $scope) {
163
+ if (((string)$scope['module']) != $this->getModuleName() || ((string)$scope['version']) != $this->getVersion()) {
164
+ return;
165
+ }
166
+
167
+ /* @var $configHelper Mana_Db_Helper_Config */
168
+ $configHelper = Mage::helper('mana_db/config');
169
+
170
+ if (!empty($scope->editable_in_grid)) {
171
+
172
+ $fieldName = 'edit_session_id';
173
+ $scope->fields->$fieldName->type = 'bigint';
174
+ $field = $scope->fields->$fieldName;
175
+ $field->default_value = 0;
176
+ $field->foreign->entity = 'mana_db/edit_session';
177
+ $field->foreign->field = 'id';
178
+ $field->foreign->on_update = 'cascade';
179
+ $field->foreign->on_delete = 'cascade';
180
+ $field->role = Mana_Db_Helper_Config::ROLE_GRID_EDITING;
181
+ $configHelper->propagateName($field);
182
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
183
+
184
+ $fieldName = 'edit_status';
185
+ $scope->fields->$fieldName->type = 'bigint';
186
+ $field = $scope->fields->$fieldName;
187
+ $field->default_value = 0;
188
+ $field->indexed = 1;
189
+ $field->role = Mana_Db_Helper_Config::ROLE_GRID_EDITING;
190
+ $configHelper->propagateName($field);
191
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
192
+
193
+ $fieldName = 'edit_massaction';
194
+ $scope->fields->$fieldName->type = 'tinyint';
195
+ $field = $scope->fields->$fieldName;
196
+ $field->default_value = 0;
197
+ $field->indexed = 1;
198
+ $field->role = Mana_Db_Helper_Config::ROLE_GRID_EDITING;
199
+ $configHelper->propagateName($field);
200
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
201
+
202
+ }
203
+ }
204
+
205
+ /**
206
+ * @param Varien_Object $context
207
+ * @param Varien_Simplexml_Element $module
208
+ * @param Varien_Simplexml_Element $entity
209
+ * @param Varien_Simplexml_Element $scope
210
+ */
211
+ public function _createFlatInfrastructure($context, $module, $entity, $scope) {
212
+ if (((string)$scope['module']) != $this->getModuleName() || ((string)$scope['version']) != $this->getVersion()) {
213
+ return;
214
+ }
215
+
216
+ /* @var $configHelper Mana_Db_Helper_Config */
217
+ $configHelper = Mage::helper('mana_db/config');
218
+
219
+ if (!empty($scope->flattens)) {
220
+ $flattenedScopeXml = $configHelper->getScopeXml((string)$scope->flattens);
221
+ if (!empty($flattenedScopeXml->store_specifics_for)) {
222
+ $global = (string)$flattenedScopeXml->store_specifics_for;
223
+ $fieldName = 'global_id';
224
+ $scope->fields->$fieldName->type = 'bigint';
225
+ $field = $scope->fields->$fieldName;
226
+ $field->foreign->entity = substr($global, 0, strrpos($global, '/')).'/flat';
227
+ $field->foreign->field = 'id';
228
+ $field->foreign->on_update = 'cascade';
229
+ $field->foreign->on_delete = 'cascade';
230
+ $field->role = Mana_Db_Helper_Config::ROLE_STORE_SPECIFICS;
231
+ $field->default_formula = '{{= global.id }}';
232
+ $configHelper->propagateName($field);
233
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
234
+
235
+ $fieldName = 'store_id';
236
+ $scope->fields->$fieldName->type = 'smallint(5) unsigned';
237
+ $field = $scope->fields->$fieldName;
238
+ $field->foreign->entity = 'core/store';
239
+ $field->foreign->field = 'store_id';
240
+ $field->foreign->on_update = 'cascade';
241
+ $field->foreign->on_delete = 'cascade';
242
+ $field->role = Mana_Db_Helper_Config::ROLE_STORE_SPECIFICS;
243
+ $field->default_formula = '{{= store.store_id }}';
244
+ $configHelper->propagateName($field);
245
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
246
+
247
+ $fieldName = 'primary_global_id';
248
+ $scope->fields->$fieldName->type = 'bigint';
249
+ $field = $scope->fields->$fieldName;
250
+ $field->foreign->entity = (string)$flattenedScopeXml->store_specifics_for;
251
+ $field->foreign->field = 'id';
252
+ $field->role = Mana_Db_Helper_Config::ROLE_FLAT;
253
+ $field->default_formula = '{{= global.primary.id }}';
254
+ $field->nullable = 1;
255
+ $configHelper->propagateName($field);
256
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
257
+
258
+ $fieldName = 'primary_id';
259
+ $scope->fields->$fieldName->type = 'bigint';
260
+ $field = $scope->fields->$fieldName;
261
+ $field->foreign->entity = (string)$scope->flattens;
262
+ $field->foreign->field = 'id';
263
+ $field->foreign->on_update = 'cascade';
264
+ $field->foreign->on_delete = 'cascade';
265
+ $field->role = Mana_Db_Helper_Config::ROLE_FLAT;
266
+ $field->default_formula = '{{= primary.id }}';
267
+ $field->nullable = 1;
268
+ $configHelper->propagateName($field);
269
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
270
+
271
+ $scope->unique->primary->global_id = '';
272
+ $scope->unique->primary->store_id = '';
273
+ }
274
+ else {
275
+ $fieldName = 'primary_id';
276
+ $scope->fields->$fieldName->type = 'bigint';
277
+ $field = $scope->fields->$fieldName;
278
+ $field->foreign->entity = (string)$scope->flattens;
279
+ $field->foreign->field = 'id';
280
+ $field->foreign->on_update = 'cascade';
281
+ $field->foreign->on_delete = 'cascade';
282
+ $field->role = Mana_Db_Helper_Config::ROLE_FLAT;
283
+ $field->default_formula = '{{= primary.id }}';
284
+ $field->nullable = 1;
285
+ $field->unique = 1;
286
+ $configHelper->propagateName($field);
287
+ $configHelper->propagateAttributes($scope, $field, array('module', 'version'));
288
+ }
289
+ }
290
+ }
291
+
292
+ /**
293
+ * @param Varien_Object $context
294
+ * @param Varien_Simplexml_Element $module
295
+ * @param Varien_Simplexml_Element $entity
296
+ * @param Varien_Simplexml_Element $scope
297
+ */
298
+ public function _beginTableScript($context, $module, $entity, $scope) {
299
+ $context->setFields(array());
300
+ $context->setIndexes(array());
301
+ $context->setConstraints(array());
302
+ }
303
+
304
+ /**
305
+ * @param Varien_Object $context
306
+ * @param Varien_Simplexml_Element $module
307
+ * @param Varien_Simplexml_Element $entity
308
+ * @param Varien_Simplexml_Element $scope
309
+ * @param Varien_Simplexml_Element $field
310
+ */
311
+ public function _fieldScript($context, $module, $entity, $scope, $field) {
312
+ if (((string)$field['module']) != $this->getModuleName() || ((string)$field['version']) != $this->getVersion()) {
313
+ return;
314
+ }
315
+ $fields = $context->getFields();
316
+ $fields[] = $field;
317
+ $context->setFields($fields);
318
+
319
+ if (isset($field->indexed) || isset($field->foreign) || !empty($field->primary)) {
320
+ $indexes = $context->getIndexes();
321
+ $indexes[] = $field;
322
+ $context->setIndexes($indexes);
323
+ }
324
+
325
+ if (isset($field->foreign)) {
326
+ $constraints = $context->getConstraints();
327
+ $constraints[] = $field;
328
+ $context->setConstraints($constraints);
329
+ }
330
+ }
331
+
332
+ /**
333
+ * @param Varien_Object $context
334
+ * @param Varien_Simplexml_Element $module
335
+ * @param Varien_Simplexml_Element $entity
336
+ * @param Varien_Simplexml_Element $scope
337
+ */
338
+ public function _endTableScript($context, $module, $entity, $scope) {
339
+ $scope = $scope;
340
+ if (!empty($scope->unique)) {
341
+ /* @var $resource Mage_Core_Model_Mysql4_Resource */
342
+ $resource = Mage::getResourceSingleton('core/resource');
343
+
344
+ $indexes = $context->getIndexes();
345
+ foreach ($scope->unique->children() as $unique) {
346
+ $index = (object)array('unique' => 1, 'name' => 'unique_'.$unique->getName(), 'indexed_fields' => array());
347
+ $includeUniqueIndex = false;
348
+ foreach ($unique->children() as $field => $def) {
349
+ if (((string)$def['module']) == $this->getModuleName() && ((string)$def['version']) == $this->getVersion()) {
350
+ $includeUniqueIndex = true;
351
+ $index->indexed_fields[] = $field;
352
+ }
353
+ else {
354
+ $installedVersion = $resource->getDbVersion(((string)$def['module']).'_setup');
355
+ if ($installedVersion && version_compare($installedVersion, (string)$def['version']) >= 0) {
356
+ $index->indexed_fields[] = $field;
357
+ }
358
+ }
359
+ }
360
+ if ($includeUniqueIndex) {
361
+ if (!(((string)$scope->unique['module']) == $this->getModuleName() && ((string)$scope->unique['version']) == $this->getVersion())) {
362
+ $index->rebuild = true;
363
+ }
364
+ $indexes[] = $index;
365
+ }
366
+ }
367
+ $context->setIndexes($indexes);
368
+ }
369
+
370
+ $sql = $context->getSql();
371
+ $context->setTable(
372
+ $this->getTable(((string)$module->name) . '/' .
373
+ ((string)$entity->name) . '/' .
374
+ ((string)$scope->name)));
375
+ if (((string)$scope['module']) != $this->getModuleName() || ((string)$scope['version']) != $this->getVersion()) {
376
+ foreach ($context->getFields() as $field) {
377
+ $sql .= "ALTER TABLE `{$context->getTable()}` ADD COLUMN ( ";
378
+ $sql .= $this->_renderField($field);
379
+ $sql .= ");\n";
380
+ }
381
+ foreach ($context->getIndexes() as $index) {
382
+ if (!empty($index->rebuild)) {
383
+ $sql .= "ALTER TABLE `{$context->getTable()}` DROP KEY `".(string)$index->name."`";
384
+ $sql .= ";\n";
385
+ }
386
+ $sql .= "ALTER TABLE `{$context->getTable()}` ADD ";
387
+ $sql .= $this->_renderIndex($index);
388
+ $sql .= ";\n";
389
+ }
390
+ }
391
+ else {
392
+ if (count($context->getFields()) || count($context->getIndexes())) {
393
+ $sql .= "DROP TABLE IF EXISTS `{$context->getTable()}`;\n";
394
+ $sql .= "CREATE TABLE `{$context->getTable()}` ( \n";
395
+ $sep = false;
396
+ foreach ($context->getFields() as $field) {
397
+ if ($sep) $sql .= ", \n"; else $sep = true;
398
+ $sql .= " ".$this->_renderField($field);
399
+ }
400
+ foreach ($context->getIndexes() as $index) {
401
+ if ($sep) $sql .= ", \n"; else $sep = true;
402
+ $sql .= " " .$this->_renderIndex($index);
403
+ }
404
+ $sql .= "\n";
405
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';\n";
406
+ }
407
+ }
408
+ foreach ($context->getConstraints() as $constraint) {
409
+ $sql .= "ALTER TABLE `{$context->getTable()}` ADD CONSTRAINT ";
410
+ $sql .= $this->_renderConstraint($context, $constraint);
411
+ $sql .= ";\n";
412
+ }
413
+
414
+ $context->setSql($sql);
415
+ }
416
+
417
+ protected function _renderField($field) {
418
+ $sql = '';
419
+ $sql .= "`".((string)$field->name)."` ";
420
+ $sql .= ((string)$field->type) ." ";
421
+ if (isset($field->nullable)) {
422
+ $sql .= "null ";
423
+ }
424
+ else {
425
+ $sql .= "NOT null ";
426
+ if (!empty($field->default_value) && strpos(strtolower((string)$field->type), 'text') === false) {
427
+ $sql .= "DEFAULT '".((string)$field->default_value)."' ";
428
+ }
429
+ elseif (strpos((string)$field->type, 'varchar') !== false) {
430
+ $sql .= "DEFAULT '' ";
431
+ }
432
+ }
433
+ if (!empty($field->primary)) {
434
+ $sql .= "AUTO_INCREMENT ";
435
+ }
436
+ return $sql;
437
+ }
438
+
439
+ protected function _renderIndex($field) {
440
+ $sql = '';
441
+ if (!empty($field->primary)) {
442
+ $sql .= "PRIMARY KEY ";
443
+ }
444
+ elseif (!empty($field->unique)) {
445
+ $sql .= "UNIQUE KEY `" . ((string)$field->name) . "` ";
446
+ }
447
+ else {
448
+ $sql .= "KEY `" . ((string)$field->name) . "` ";
449
+ }
450
+ if (empty($field->indexed_fields)) {
451
+ $sql .= "(`" . ((string)$field->name) . "`) ";
452
+ }
453
+ else {
454
+ $indexedFields = array();
455
+ foreach ($field->indexed_fields as $indexedField) {
456
+ $indexedFields[] = "`$indexedField`";
457
+ }
458
+ $sql .= "(" . implode(', ', $indexedFields) . ") ";
459
+ }
460
+
461
+ return $sql;
462
+ }
463
+
464
+ protected function _renderConstraint($context, $field) {
465
+ $sql = '';
466
+ $sql .= "`FK_{$context->getTable()}_". ((string)$field->name)."` ";
467
+ $sql .= "FOREIGN KEY (`" . ((string)$field->name). "`) ";
468
+ $sql .= "REFERENCES `{$this->getTable((string)$field->foreign->entity)}` (`".((string)$field->foreign->field)."`) ";
469
+ if (!empty($field->foreign->on_delete)) {
470
+ $sql .= "ON DELETE " . ((string)$field->foreign->on_delete) . " ";
471
+ }
472
+ if (!empty($field->foreign->on_update)) {
473
+ $sql .= "ON UPDATE ".((string)$field->foreign->on_update)." ";
474
+ }
475
+
476
+ return $sql;
477
+ }
478
+ }
app/code/local/Mana/Db/Resource/Edit/Session.php CHANGED
@@ -45,4 +45,15 @@ class Mana_Db_Resource_Edit_Session extends Mage_Core_Model_Mysql4_Abstract {//M
45
  throw $e;
46
  }
47
  }
 
 
 
 
 
 
 
 
 
 
 
48
  }
45
  throw $e;
46
  }
47
  }
48
+
49
+ public function isExpired($editSessionId) {
50
+ $db = $this->_getReadAdapter();
51
+ $table = Mage::getSingleton('core/resource')->getTableName('mana_db/edit_session');
52
+
53
+ $select = $db->select()
54
+ ->from($table, 'id')
55
+ ->where("id = ?", $editSessionId);
56
+
57
+ return $db->fetchOne($select) ? false : true;
58
+ }
59
  }
app/code/local/Mana/Db/Resource/Entity.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method string getScopeName()
11
+ *
12
+ */
13
+ class Mana_Db_Resource_Entity extends Mage_Core_Model_Mysql4_Abstract {
14
+ protected $_scope;
15
+
16
+ public function __construct($data = null) {
17
+ if (is_array($data)) {
18
+ if (isset($data['scope'])) {
19
+ $this->_scope = $data['scope'];
20
+ }
21
+ }
22
+
23
+ parent::__construct();
24
+ }
25
+
26
+ /**
27
+ * @param Mana_Db_Model_Entity $object
28
+ * @param int $id
29
+ * @param int $storeId
30
+ * @param string $fieldName
31
+ * @return Mana_Db_Resource_Entity
32
+ */
33
+ public function loadForStore($object, $id, $storeId, $fieldName) {
34
+ $read = $this->_getReadAdapter();
35
+ $select = $this->_getReadAdapter()->select()
36
+ ->from($this->getMainTable())
37
+ ->where("{$this->getMainTable()}.`$fieldName` = ?", $id)
38
+ ->where("{$this->getMainTable()}.`store_id` = ?", $storeId);
39
+ $data = $read->fetchRow($select);
40
+
41
+ if ($data) {
42
+ $object->setData($data);
43
+ }
44
+
45
+ $this->unserializeFields($object);
46
+ $this->_afterLoad($object);
47
+
48
+ return $this;
49
+ }
50
+
51
+ protected function _construct() {
52
+ $this->_initScope();
53
+ }
54
+ protected function _initScope() {
55
+
56
+ /* @var $db Mana_Db_Helper_Data */
57
+ $db = Mage::helper('mana_db');
58
+
59
+ $this->_init($db->getScopedName($this->_scope), 'id');
60
+ return $this;
61
+ }
62
+
63
+ /**
64
+ * @param Mana_Db_Model_Entity $object
65
+ * @param int $id
66
+ * @param int $sessionId
67
+ * @return Mana_Db_Resource_Entity
68
+ */
69
+ public function loadEdited($object, $id, $sessionId) {
70
+ $read = $this->_getReadAdapter();
71
+ $select = $read->select()
72
+ ->from($this->getMainTable())
73
+ ->where("{$this->getMainTable()}.`edit_status` = ?", $id)
74
+ ->where("{$this->getMainTable()}.`edit_session_id` = ?", $sessionId);
75
+ $data = $read->fetchRow($select);
76
+
77
+ if ($data) {
78
+ $object->setData($data);
79
+ }
80
+
81
+ $this->unserializeFields($object);
82
+ $this->_afterLoad($object);
83
+
84
+ return $this;
85
+ }
86
+ }
app/code/local/Mana/Db/Resource/Entity/Collection.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method string getScopeName()
11
+ */
12
+ class Mana_Db_Resource_Entity_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
13
+ protected $_scope;
14
+ protected $_editFilter = null;
15
+ protected $_parentCondition = '';
16
+
17
+ public function __construct($resource = null) {
18
+ if (is_array($resource)) {
19
+ if (isset($resource['scope'])) {
20
+ $this->_scope = $resource['scope'];
21
+ }
22
+ $resource = isset($resource['resource']) ? $resource['resource'] : null;
23
+ }
24
+
25
+ parent::__construct($resource);
26
+ }
27
+
28
+ protected function _construct() {
29
+ $this->_initScope();
30
+ }
31
+ protected function _initScope() {
32
+
33
+ /* @var $db Mana_Db_Helper_Data */
34
+ $db = Mage::helper('mana_db');
35
+
36
+ $this->_init($this->_scope);
37
+ return $this;
38
+ }
39
+
40
+ /**
41
+ * Get resource instance
42
+ *
43
+ * @return Mage_Core_Model_Mysql4_Abstract
44
+ */
45
+ public function getResource() {
46
+ if (empty($this->_resource)) {
47
+ /* @var $db Mana_Db_Helper_Data */
48
+ $db = Mage::helper('mana_db');
49
+
50
+ $this->_resource = $db->getResourceSingleton($this->getResourceModelName());
51
+ }
52
+
53
+ return $this->_resource;
54
+ }
55
+
56
+ public function getNewEmptyItem() {
57
+ /* @var $db Mana_Db_Helper_Data */
58
+ $db = Mage::helper('mana_db');
59
+
60
+ return $db->getModel($this->_model);
61
+ }
62
+
63
+ public function setEditFilter($editFilter, $parentCondition = '') {
64
+ $this->_editFilter = $editFilter;
65
+ $this->_parentCondition = $parentCondition;
66
+
67
+ return $this;
68
+ }
69
+
70
+ public function setStoreFilter($storeId = null) {
71
+ if ($storeId === null) {
72
+ $storeId = Mage::app()->getStore()->getId();
73
+ }
74
+
75
+ $this->getSelect()->where("`main_table`.`store_id` = ?", $storeId);
76
+ return $this;
77
+ }
78
+
79
+ protected function _beforeLoad() {
80
+ $this->_renderEditFilter();
81
+ parent::_beforeLoad();
82
+
83
+ return $this;
84
+ }
85
+
86
+ protected function _renderEditFilter($select = null) {
87
+ if (!$select) {
88
+ $select = $this->getSelect();
89
+ }
90
+ $alias = 'main_table';
91
+ if (is_array($this->_editFilter)) {
92
+ $sql = count($this->_editFilter['saved'])
93
+ ? $this->getConnection()->quoteInto("$alias.edit_status = 0 AND $alias.id NOT IN (?)", array_keys($this->_editFilter['saved']))
94
+ : "$alias.edit_status = 0";
95
+ if ($this->_parentCondition) {
96
+ $sql .= " AND ($alias.{$this->_parentCondition})";
97
+ }
98
+ if (count($this->_editFilter['saved'])) {
99
+ $sql = "($sql) OR ({$this->getConnection()->quoteInto(
100
+ "$alias.edit_status <> 0 AND $alias.edit_session_id = ?", $this->_editFilter['sessionId'])})";
101
+ }
102
+ if (count($this->_editFilter['deleted'])) {
103
+ $sql = "($sql)" .
104
+ " AND ({$this->getConnection()->quoteInto("$alias.id NOT IN (?)", $this->_editFilter['deleted'])})";
105
+ //" AND ({$this->getConnection()->quoteInto("$alias.edit_status NOT IN (?)", $this->_editFilter['deleted'])})";
106
+ }
107
+ $select->where($sql);
108
+ }
109
+ elseif ($this->_editFilter) {
110
+ $select->where("$alias.edit_status = 0");
111
+ if ($this->_parentCondition) {
112
+ $select->where("$alias.{$this->_parentCondition}");
113
+ }
114
+ }
115
+
116
+ return $this;
117
+ }
118
+
119
+ public function getSelectCountSql() {
120
+ $sql = parent::getSelectCountSql();
121
+ $this->_renderEditFilter($sql);
122
+ return $sql;
123
+ }
124
+ }
app/code/local/Mana/Db/Resource/Entity/Indexer.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+
13
+ class Mana_Db_Resource_Entity_Indexer extends Mage_Core_Model_Mysql4_Abstract {
14
+ /**
15
+ * Resource initialization
16
+ */
17
+ protected function _construct() {
18
+ $this->_setResource('core');
19
+ }
20
+
21
+ /**
22
+ * @param Mana_Db_Model_Entity_Indexer $indexer provides access to process record and indexer setup
23
+ * @param Varien_Simplexml_Element $target setup in config.xml
24
+ * @param Varien_Simplexml_Element $scope setup in m_db.xml
25
+ * @param array $options on which records to run
26
+ * @return void
27
+ */
28
+ public function flattenScope($indexer, $target, $scope, $options) {
29
+ /** @noinspection PhpUndefinedFieldInspection */
30
+ $targetEntity = ((string)$target->entity) . '/' . $scope->getName();
31
+
32
+ if (isset($options['entity_filters']) && !isset($options['entity_filters'][$targetEntity])) {
33
+ return;
34
+ }
35
+ $options = array_merge(array(
36
+ 'provide_field_details_in_exceptions' => true,
37
+ ), $options);
38
+ if (isset($options['entity_filters']) && !isset($options['entity_filter_formula'])) {
39
+ $options['entity_filter_formula'] = '{{= ' . $options['entity_filters'][$targetEntity] . '}}';
40
+ }
41
+ $db = $this->_getWriteAdapter();
42
+ /* @var $res Mage_Core_Model_Resource */
43
+ $res = Mage::getSingleton('core/resource');
44
+ /* @var $dbHelper Mana_Db_Helper_Data */
45
+ $dbHelper = Mage::helper('mana_db');
46
+ /* @var $formulaHelper Mana_Db_Helper_Formula */
47
+ $formulaHelper = Mage::helper('mana_db/formula');
48
+
49
+
50
+ // get basic select from all source tables, properly joined (based on m_db.xml)
51
+ /** @noinspection PhpUndefinedFieldInspection */
52
+ $entity = (string)$scope->flattens;
53
+ //$db->query($formulaHelper->delete($entity, $targetEntity));
54
+
55
+ // get formula hashes and formula texts
56
+ $formulaGroups = $formulaHelper->getFormulaGroups($targetEntity, $options);
57
+
58
+ // for each formula hash => formula text
59
+ foreach ($formulaGroups as $formulas) {
60
+ $formulas = $formulas ? json_decode($formulas, true) : array();
61
+
62
+ // filter basic select by formula hash
63
+ $context = $formulaHelper->select($targetEntity, $formulas, $options);
64
+
65
+ // convert SELECT into UPDATE which acts as INSERT on DUPLICATE unique keys
66
+ $sql = $context->getSelect()->insertFromSelect(
67
+ $res->getTableName($dbHelper->getScopedName($targetEntity)),
68
+ $context->getFields());
69
+
70
+ // run the statement
71
+ try {
72
+ $db->query($sql);
73
+ }
74
+ catch (Exception $e) {
75
+ /* @var $logger Mana_Core_Helper_Logger */
76
+ $logger = Mage::helper('mana_core/logger');
77
+ $logger->logDbIndexerFailure($sql);
78
+ throw $e;
79
+ }
80
+
81
+ }
82
+ }
83
+
84
+ protected function _getUpdateSelect($basicSelect, $formulaHash) {
85
+ $select = clone $basicSelect;
86
+
87
+ return $select;
88
+ }
89
+ }
app/code/local/Mana/Db/Resource/Entity/JsonCollection.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ * @method string getScopeName()
11
+ */
12
+ class Mana_Db_Resource_Entity_JsonCollection extends Varien_Data_Collection {
13
+ protected $_rawData = array();
14
+ protected $_data = array();
15
+ protected $_mFilters = array();
16
+
17
+ /**
18
+ * @param $data
19
+ * @return Mana_Db_Resource_Entity_JsonCollection
20
+ */
21
+ public function setData($data) {
22
+ foreach (array_keys($data) as $index) {
23
+ if (!isset($data[$index]['id'])) {
24
+ $data[$index]['id'] = $index;
25
+ }
26
+ }
27
+ $this->_data = $data;
28
+ $this->_rawData = $data;
29
+
30
+ return $this;
31
+ }
32
+
33
+ public function getData() {
34
+ return $this->_data;
35
+ }
36
+ public function load($printQuery = false, $logQuery = false) {
37
+ if ($this->isLoaded()) {
38
+ return $this;
39
+ }
40
+
41
+ $this->_beforeLoad();
42
+
43
+ $this->_renderFilters()
44
+ ->_renderOrders()
45
+ ->_renderLimit();
46
+
47
+ $data = $this->getData();
48
+
49
+ if (is_array($data)) {
50
+ foreach ($data as $row) {
51
+ $item = $this->getNewEmptyItem();
52
+ $item->addData($row);
53
+ $this->addItem($item);
54
+ }
55
+ }
56
+
57
+ $this->_setIsLoaded();
58
+ $this->_afterLoad();
59
+
60
+ return $this;
61
+ }
62
+
63
+ protected function _beforeLoad() {
64
+ return $this;
65
+ }
66
+
67
+ protected function _afterLoad() {
68
+ return $this;
69
+ }
70
+
71
+ /**
72
+ * @return Mana_Db_Resource_Entity_JsonCollection
73
+ */
74
+ protected function _renderFilters() {
75
+ $this->_data = array_filter($this->_data, array($this, '_filterItem'));
76
+ return $this;
77
+ }
78
+
79
+ /**
80
+ * Render sql select orders
81
+ *
82
+ * @return Mana_Db_Resource_Entity_JsonCollection
83
+ */
84
+ protected function _renderOrders() {
85
+ if (count($this->_orders)) {
86
+ uasort($this->_data, array($this, '_compareItems'));
87
+ }
88
+
89
+ return $this;
90
+ }
91
+
92
+ /**
93
+ * Render sql select limit
94
+ *
95
+ * @return Mana_Db_Resource_Entity_JsonCollection
96
+ */
97
+ protected function _renderLimit() {
98
+ if ($this->_pageSize !== false) {
99
+ $offset = ($this->_curPage - 1) * $this->_pageSize;
100
+ $count = count($this->_data);
101
+ if ($count > $offset) {
102
+ if ($count >= $offset + $this->_pageSize) {
103
+ $this->_data = array_slice($this->_data, $offset, $this->_pageSize);
104
+ }
105
+ else {
106
+ $this->_data = array_slice($this->_data, $offset);
107
+ }
108
+ }
109
+ else {
110
+ $this->_data = array();
111
+ }
112
+ }
113
+
114
+ return $this;
115
+ }
116
+
117
+ public function getRawData() {
118
+ return $this->_rawData;
119
+ }
120
+
121
+ /**
122
+ * @param array $a
123
+ * @param array $b
124
+ * @return int
125
+ */
126
+ protected function _compareItems($a, $b) {
127
+ foreach ($this->_orders as $column => $direction) {
128
+ if (isset($a[$column])) {
129
+ if (isset($b[$column])) {
130
+ if ($a[$column] < $b[$column]) {
131
+ return strtolower($direction) == 'desc' ? 1 : -1;
132
+ }
133
+ if ($a[$column] > $b[$column]) {
134
+ return strtolower($direction) == 'desc' ? -1 : 1;
135
+ }
136
+ }
137
+ else {
138
+ return strtolower($direction) == 'desc' ? -1 : 1;
139
+ }
140
+ }
141
+ else {
142
+ if (isset($b[$column])) {
143
+ return strtolower($direction) == 'desc' ? 1 : -1;
144
+ }
145
+ }
146
+ }
147
+ return 0;
148
+ }
149
+
150
+ /**
151
+ * @param array $a
152
+ * @return bool
153
+ */
154
+ protected function _filterItem($a) {
155
+ foreach ($this->_mFilters as $filter) {
156
+ $value = isset($a[$filter['attribute']]) ? $a[$filter['attribute']] : '';
157
+ if (isset($filter['condition']['like'])) {
158
+ $test = $filter['condition']['like'];
159
+ if ($this->getMbstring()->stripos($value, $this->getMbstring()->substr($test, 1, mb_strlen($test) - 2)) === false) {
160
+ return false;
161
+ }
162
+ }
163
+ elseif (isset($filter['condition']['eq'])) {
164
+ $test = $filter['condition']['eq'];
165
+ if ($value != $test) {
166
+ return false;
167
+ }
168
+ }
169
+ }
170
+
171
+ return true;
172
+ }
173
+ public function addFieldToFilter($attribute, $condition = null) {
174
+ $this->_mFilters[] = array('attribute' => $attribute, 'condition' => $condition);
175
+ }
176
+
177
+ #region Dependencies
178
+
179
+ /**
180
+ * @return Mana_Core_Helper_Mbstring
181
+ */
182
+ public function getMbstring() {
183
+ return Mage::helper('mana_core/mbstring');
184
+ }
185
+ #endregion
186
+ }
app/code/local/Mana/Db/Resource/Formula.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Db_Resource_Formula extends Mage_Core_Model_Mysql4_Abstract {
13
+ /**
14
+ * Resource initialization
15
+ */
16
+ protected function _construct() {
17
+ $this->_setResource('core');
18
+ }
19
+
20
+ /**
21
+ * @param Mana_Db_Model_Formula_Context $context
22
+ * @param Mana_Db_Model_Formula_Expr $expr
23
+ * @return Varien_Db_Select
24
+ */
25
+ public function getAggregateSubSelect($context, $expr) {
26
+ /* @var $db Varien_Db_Adapter_Pdo_Mysql */
27
+ $db = $this->getReadConnection();
28
+
29
+ $select = $db->select();
30
+ throw new Exception('Not implemented');
31
+ }
32
+
33
+ public function getTableFields($entity) {
34
+ /* @var $dbHelper Mana_Db_Helper_Data */
35
+ $dbHelper = Mage::helper('mana_db');
36
+
37
+ return $this->getReadConnection()->describeTable($this->getTable($dbHelper->getScopedName($entity)));
38
+ }
39
+
40
+ /**
41
+ * @return Varien_Db_Select
42
+ */
43
+ public function select() {
44
+ /* @var $db Varien_Db_Adapter_Pdo_Mysql */
45
+ $db = $this->getReadConnection();
46
+
47
+ return $db->select();
48
+ }
49
+
50
+ }
app/code/local/Mana/Db/etc/config.xml CHANGED
@@ -12,7 +12,7 @@
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,
@@ -72,6 +72,12 @@
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) -->
@@ -138,7 +144,7 @@
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>
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>13.09.25.14</version>
16
  </Mana_Db>
17
  </modules>
18
  <!-- This section contains module settings which are merged into global configuration during each page load,
72
  <mana_db_replicator>
73
  <model>mana_db/indexer</model>
74
  </mana_db_replicator>
75
+ <mana_db translate="name description" module="mana_db">
76
+ <model>mana_db/entity_indexer</model>
77
+ <name>Entity Flat Data (MANAdev)</name>
78
+ <description>Reorganizes entity data to flat structure</description>
79
+ <targets/>
80
+ </mana_db>
81
  </indexer>
82
  </index>
83
  <!-- BASED ON SNIPPET: New Models/Event support (config.xml) -->
144
  <observers>
145
  <mana_db>
146
  <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
147
+ <method>afterSaveCommit</method> <!-- event handler method name -->
148
  </mana_db>
149
  </observers>
150
  </model_save_commit_after>
app/code/local/Mana/Db/etc/m_db.xml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <modules>
11
+ <eav>
12
+ <tables>
13
+ <attribute_option>
14
+ <fields>
15
+ <global_label>
16
+ <formula>
17
+ <expr>{{= value.value }}</expr>
18
+ <join>
19
+ <value>
20
+ <type>left</type>
21
+ <entity>eav/attribute_option_value</entity>
22
+ <on>{{= value.option_id }} = {{= context.option_id }} AND {{= value.store_id }} = 0</on>
23
+ </value>
24
+ </join>
25
+ </formula>
26
+ </global_label>
27
+ <store_label>
28
+ <formula>
29
+ <expr>{{= value.value }}</expr>
30
+ <join>
31
+ <value>
32
+ <type>left</type>
33
+ <entity>eav/attribute_option_value</entity>
34
+ <on>{{= value.option_id }} = {{= context.option_id }} AND {{= value.store_id }} = {{= store.store_id }}</on>
35
+ </value>
36
+ </join>
37
+ </formula>
38
+ </store_label>
39
+ </fields>
40
+ </attribute_option>
41
+ </tables>
42
+ </eav>
43
+ </modules>
44
+ </config>
app/code/local/Mana/Filters/Block/Filter.php CHANGED
@@ -9,6 +9,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
  protected $_isBlockPrepared = false;
@@ -66,17 +68,36 @@ class Mana_Filters_Block_Filter extends Mage_Catalog_Block_Layer_Filter_Abstract
66
  foreach ($this->getItems() as $item) {
67
  /* @var $item Mana_Filters_Model_Item */
68
  if ($item->getMSelected()) {
69
- $result[] = $item->getSeoValue();
70
  }
71
  }
72
- return implode('_', $result);
 
73
  }
74
 
 
 
 
 
75
  public function getItemsCount() {
76
  $this->_prepareFilterBlockOnce();
77
  return $this->getHidden() ? 0 : $this->_filter->getItemsCount();
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  protected function _initFilter() {
81
  if ($filter = Mage::getSingleton('mana_filters/repository')->getFilter($this->getFilterOptions()->getCode())) {
82
  $this->_filter = $filter;
@@ -103,4 +124,31 @@ class Mana_Filters_Block_Filter extends Mage_Catalog_Block_Layer_Filter_Abstract
103
  }
104
  return $this;
105
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  }
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
+ * @method Mana_Filters_Model_Filter2_Store getFilterOptions()
14
  */
15
  class Mana_Filters_Block_Filter extends Mage_Catalog_Block_Layer_Filter_Abstract {
16
  protected $_isBlockPrepared = false;
68
  foreach ($this->getItems() as $item) {
69
  /* @var $item Mana_Filters_Model_Item */
70
  if ($item->getMSelected()) {
71
+ $result[$item->getSeoValue()] = $item->getSeoData();
72
  }
73
  }
74
+
75
+ return $this->jsonHelper()->encodeAttribute($result);
76
  }
77
 
78
+ public function getPopupBlockName() {
79
+ $displayOptions = $this->getFilterOptions()->getDisplayOptions();
80
+ return (string)$displayOptions->popup_class;
81
+ }
82
  public function getItemsCount() {
83
  $this->_prepareFilterBlockOnce();
84
  return $this->getHidden() ? 0 : $this->_filter->getItemsCount();
85
  }
86
 
87
+ public function getMultipleValueSeparator() {
88
+ if (((string)Mage::getConfig()->getNode('modules/ManaPro_FilterSeoLinks/active')) == 'true') {
89
+ /* @var $seo Mana_Seo_Helper_Data */
90
+ $seo = Mage::helper('mana_seo');
91
+
92
+ $schema = $seo->getActiveSchema(Mage::app()->getStore()->getId());
93
+
94
+ return $schema->getMultipleValueSeparator();
95
+ }
96
+ else {
97
+ return '_';
98
+ }
99
+ }
100
+
101
  protected function _initFilter() {
102
  if ($filter = Mage::getSingleton('mana_filters/repository')->getFilter($this->getFilterOptions()->getCode())) {
103
  $this->_filter = $filter;
124
  }
125
  return $this;
126
  }
127
+
128
+ public function getListItemClass($item) {
129
+ $result = '';
130
+ if ($item->getMShowSelected()) {
131
+ $result .= 'm-selected-ln-item';
132
+ }
133
+ if (!($item->getCount() || $item->getMSelected())) {
134
+ if ($result) {
135
+ $result .= ' ';
136
+ }
137
+ $result .= 'm-disabled';
138
+ }
139
+ if ($result) {
140
+ $result = "class=\"$result\"";
141
+ }
142
+ return $result;
143
+ }
144
+
145
+ #region Dependencies
146
+
147
+ /**
148
+ * @return Mana_Core_Helper_Json
149
+ */
150
+ public function jsonHelper() {
151
+ return Mage::helper('mana_core/json');
152
+ }
153
+ #endregion
154
  }
app/code/local/Mana/Filters/Block/View.php CHANGED
@@ -29,7 +29,7 @@ class Mana_Filters_Block_View extends Mage_Catalog_Block_Layer_View {
29
  {
30
  /* @var $layoutHelper Mana_Core_Helper_Layout */
31
  $layoutHelper = Mage::helper('mana_core/layout');
32
- $layoutHelper->delayPrepareLayout($this, 1000);
33
 
34
  return $this;
35
  }
@@ -52,7 +52,7 @@ class Mana_Filters_Block_View extends Mage_Catalog_Block_Layer_View {
52
 
53
  $showState = 'all';
54
  if ($showInFilter = $this->getShowInFilter()) {
55
- if ($template = Mage::getStoreConfig('mana_filters/positioning/' . $showInFilter)) {
56
  $this->setTemplate($template);
57
  }
58
  $showState = Mage::getStoreConfig('mana_filters/positioning/show_state_' . $showInFilter);
@@ -81,7 +81,7 @@ class Mana_Filters_Block_View extends Mage_Catalog_Block_Layer_View {
81
  'query' => $query,
82
  'layer' => $layer,
83
  'attribute_model' => $filterOptions->getAttribute(),
84
- 'mode' => $helper->getMode(),
85
  ));
86
  $block->init();
87
  $this->setChild($filterOptions->getCode() . '_filter', $block);
@@ -94,6 +94,17 @@ class Mana_Filters_Block_View extends Mage_Catalog_Block_Layer_View {
94
  return $this;
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
97
  public function getFilters() {
98
  /* @var $helper Mana_Filters_Helper_Data */
99
  $helper = Mage::helper(strtolower('Mana_Filters'));
@@ -125,14 +136,14 @@ class Mana_Filters_Block_View extends Mage_Catalog_Block_Layer_View {
125
  /* @var $helper Mana_Filters_Helper_Data */
126
  $helper = Mage::helper(strtolower('Mana_Filters'));
127
 
128
- return $helper->getLayer();
129
  }
130
 
131
  public function canShowBlock() {
132
  /* @var $helper Mana_Filters_Helper_Data */
133
  $helper = Mage::helper(strtolower('Mana_Filters'));
134
 
135
- switch ($helper->getMode()) {
136
  case 'category':
137
  return $this->_canShowBlockInCategory();
138
  case 'search':
@@ -153,16 +164,29 @@ class Mana_Filters_Block_View extends Mage_Catalog_Block_Layer_View {
153
  }
154
  }
155
  public function _canShowBlockInSearch() {
156
- $_isLNAllowedByEngine = Mage::helper('catalogsearch')->getEngine()->isLeyeredNavigationAllowed();
 
 
 
 
157
  if (!$_isLNAllowedByEngine) {
158
  return false;
159
  }
160
  $availableResCount = (int) Mage::app()->getStore()
161
  ->getConfig(Mage_CatalogSearch_Model_Layer::XML_PATH_DISPLAY_LAYER_COUNT);
162
 
163
- if ($availableResCount && $availableResCount<$this->getLayer()->getProductCollection()->getSize()) {
 
 
164
  return false;
165
  }
166
  return $this->_canShowBlockInCategory();
167
  }
 
 
 
 
 
 
 
168
  }
29
  {
30
  /* @var $layoutHelper Mana_Core_Helper_Layout */
31
  $layoutHelper = Mage::helper('mana_core/layout');
32
+ $layoutHelper->delayPrepareLayout($this, 200);
33
 
34
  return $this;
35
  }
52
 
53
  $showState = 'all';
54
  if ($showInFilter = $this->getShowInFilter()) {
55
+ if (($template = Mage::getStoreConfig('mana_filters/positioning/' . $showInFilter)) && !$this->getTakeTemplateFromXml()) {
56
  $this->setTemplate($template);
57
  }
58
  $showState = Mage::getStoreConfig('mana_filters/positioning/show_state_' . $showInFilter);
81
  'query' => $query,
82
  'layer' => $layer,
83
  'attribute_model' => $filterOptions->getAttribute(),
84
+ 'mode' => $this->getMode(),
85
  ));
86
  $block->init();
87
  $this->setChild($filterOptions->getCode() . '_filter', $block);
94
  return $this;
95
  }
96
 
97
+ public function getMode() {
98
+ /* @var $helper Mana_Filters_Helper_Data */
99
+ $helper = Mage::helper(strtolower('Mana_Filters'));
100
+
101
+ if ($mode = $this->_getData('mode')) {
102
+ return $mode;
103
+ }
104
+ else {
105
+ return $helper->getMode();
106
+ }
107
+ }
108
  public function getFilters() {
109
  /* @var $helper Mana_Filters_Helper_Data */
110
  $helper = Mage::helper(strtolower('Mana_Filters'));
136
  /* @var $helper Mana_Filters_Helper_Data */
137
  $helper = Mage::helper(strtolower('Mana_Filters'));
138
 
139
+ return $helper->getLayer($this->getMode());
140
  }
141
 
142
  public function canShowBlock() {
143
  /* @var $helper Mana_Filters_Helper_Data */
144
  $helper = Mage::helper(strtolower('Mana_Filters'));
145
 
146
+ switch ($this->getMode()) {
147
  case 'category':
148
  return $this->_canShowBlockInCategory();
149
  case 'search':
164
  }
165
  }
166
  public function _canShowBlockInSearch() {
167
+ $engine = Mage::helper('catalogsearch')->getEngine();
168
+ $_isLNAllowedByEngine = $engine->isLeyeredNavigationAllowed();
169
+ if (!$_isLNAllowedByEngine && method_exists($engine, 'isLayeredNavigationAllowed')) {
170
+ $_isLNAllowedByEngine = $engine->isLayeredNavigationAllowed();
171
+ }
172
  if (!$_isLNAllowedByEngine) {
173
  return false;
174
  }
175
  $availableResCount = (int) Mage::app()->getStore()
176
  ->getConfig(Mage_CatalogSearch_Model_Layer::XML_PATH_DISPLAY_LAYER_COUNT);
177
 
178
+ if (!$this->layerHelper()->useSolr() && $availableResCount &&
179
+ $availableResCount < $this->getLayer()->getProductCollection()->getSize())
180
+ {
181
  return false;
182
  }
183
  return $this->_canShowBlockInCategory();
184
  }
185
+
186
+ /**
187
+ * @return Mana_Filters_Helper_Data
188
+ */
189
+ public function layerHelper() {
190
+ return Mage::helper('mana_filters');
191
+ }
192
  }
app/code/local/Mana/Filters/Helper/Data.php CHANGED
@@ -10,7 +10,7 @@
10
  * Generic helper functions for Mana_Filters module. This class is a must for any module even if empty.
11
  * @author Mana Team
12
  */
13
- class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
14
  /**
15
  * Return unique filter name.
16
  * OO purists would say that kind of ifs should be done using virtual functions. Here we ignore OO-ness and
@@ -73,9 +73,9 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
73
  }
74
  else {
75
  if (!$this->_filterOptionsCollection) {
76
- Mana_Core_Profiler::start('mln', __CLASS__, __METHOD__, '$productCollection->getSetIds()');
77
  $setIds = Mage::getSingleton('catalog/layer')->getProductCollection()->getSetIds();
78
- Mana_Core_Profiler::stop('mln', __CLASS__, __METHOD__, '$productCollection->getSetIds()');
79
  $this->_filterOptionsCollection = Mage::getResourceModel('mana_filters/filter2_store_collection')
80
  ->addFieldToSelect('*')
81
  ->addCodeFilter($this->_getAttributeCodes($setIds))
@@ -115,7 +115,7 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
115
  }
116
  return $url;
117
  }
118
- public function getClearUrl($markUrl = true, $clearListParams = false) {
119
  $filterState = array();
120
  foreach ($this->getLayer()->getState()->getFilters() as $item) {
121
  $filterState[$item->getFilter()->getRequestVar()] = $item->getFilter()->getCleanValue();
@@ -135,8 +135,19 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
135
  $params['_m_escape'] = '';
136
  $filterState['m-layered'] = null;
137
  $params['_query'] = $filterState;
 
 
 
138
  $result = Mage::getUrl('*/*/*', $params);
139
- if ($markUrl) {
 
 
 
 
 
 
 
 
140
  $result = $this->markLayeredNavigationUrl($result, '*/*/*', $params);
141
  }
142
  return $result;
@@ -175,17 +186,6 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
175
  }
176
  $select->setPart(Zend_Db_Select::WHERE, $where);
177
  }
178
- /**
179
- * @return Mage_Catalog_Model_Layer
180
- */
181
- public function getMode() {
182
- if (in_array(Mage::helper('mana_core')->getRoutePath(), array('catalogsearch/result/index', 'manapro_filterajax/search/index'))) {
183
- return 'search';
184
- }
185
- else {
186
- return 'category';
187
- }
188
- }
189
 
190
  /**
191
  * @param Mana_Filters_Model_Filter2_Store $filterOptions
@@ -202,26 +202,6 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
202
  throw new Exception('Not implemented');
203
  }
204
  }
205
- /**
206
- * @return Mage_Catalog_Model_Layer
207
- * @throws Exception
208
- */
209
- public function getLayer () {
210
- switch ($this->getMode()) {
211
- case 'category':
212
- return Mage::getSingleton($this->useSolrForNavigation()
213
- ? 'enterprise_search/catalog_layer'
214
- : 'catalog/layer'
215
- );
216
- case 'search':
217
- return Mage::getSingleton($this->useSolrForSearch()
218
- ? 'enterprise_search/search_layer'
219
- : 'catalogsearch/layer'
220
- );
221
- default:
222
- throw new Exception('Not implemented');
223
- }
224
- }
225
  public function canShowFilterInBlock($block, $filter) {
226
  if ($block->getData('show_'.$filter->getCode())) {
227
  return true;
@@ -229,6 +209,12 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
229
  elseif ($block->getData('hide_' . $filter->getCode())) {
230
  return false;
231
  }
 
 
 
 
 
 
232
  elseif ($showInFilter = $block->getShowInFilter()) {
233
  $showIn = $filter->getShowIn();
234
  if (!is_array($showIn)) {
@@ -397,33 +383,31 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
397
  return $result;
398
  }
399
 
400
- public function useSolrForNavigation() {
401
- if (!Mage::helper('core')->isModuleEnabled('Enterprise_Search')) {
402
- return false;
403
- }
404
- /* @var $helper Enterprise_Search_Helper_Data */
405
- $helper = Mage::helper('enterprise_search');
406
 
407
- return $helper->getIsEngineAvailableForNavigation();
 
 
 
 
 
 
 
 
 
 
 
408
  }
409
- public function useSolrForSearch() {
410
- if (!Mage::helper('core')->isModuleEnabled('Enterprise_Search')) {
411
- return false;
412
- }
413
- /* @var $helper Enterprise_Search_Helper_Data */
414
- $helper = Mage::helper('enterprise_search');
415
 
416
- return $helper->isThirdPartSearchEngine() && $helper->isActiveEngine();
417
- }
418
 
419
- public function useSolr() {
420
- switch ($this->getMode()) {
421
- case 'category':
422
- return $this->useSolrForNavigation();
423
- case 'search':
424
- return $this->useSolrForSearch();
425
- default:
426
- throw new Exception('Not implemented');
427
- }
428
  }
 
429
  }
10
  * Generic helper functions for Mana_Filters module. This class is a must for any module even if empty.
11
  * @author Mana Team
12
  */
13
+ class Mana_Filters_Helper_Data extends Mana_Core_Helper_Layer {
14
  /**
15
  * Return unique filter name.
16
  * OO purists would say that kind of ifs should be done using virtual functions. Here we ignore OO-ness and
73
  }
74
  else {
75
  if (!$this->_filterOptionsCollection) {
76
+ Mana_Core_Profiler::start('mln' . '::' . __CLASS__ . '::' . __METHOD__ . '::' . '$productCollection->getSetIds()');
77
  $setIds = Mage::getSingleton('catalog/layer')->getProductCollection()->getSetIds();
78
+ Mana_Core_Profiler::stop('mln' . '::' . __CLASS__ . '::' . __METHOD__ . '::' . '$productCollection->getSetIds()');
79
  $this->_filterOptionsCollection = Mage::getResourceModel('mana_filters/filter2_store_collection')
80
  ->addFieldToSelect('*')
81
  ->addCodeFilter($this->_getAttributeCodes($setIds))
115
  }
116
  return $url;
117
  }
118
+ public function getClearUrl($markUrl = true, $clearListParams = false, $nosid = false, $clearAllParams = false) {
119
  $filterState = array();
120
  foreach ($this->getLayer()->getState()->getFilters() as $item) {
121
  $filterState[$item->getFilter()->getRequestVar()] = $item->getFilter()->getCleanValue();
135
  $params['_m_escape'] = '';
136
  $filterState['m-layered'] = null;
137
  $params['_query'] = $filterState;
138
+ if ($nosid) {
139
+ $params['_nosid'] = true;
140
+ }
141
  $result = Mage::getUrl('*/*/*', $params);
142
+ if ($clearAllParams) {
143
+ /* @var $mbstring Mana_Core_Helper_Mbstring */
144
+ $mbstring = Mage::helper('mana_core/mbstring');
145
+
146
+ if ($pos = $mbstring->strpos($result, '?')) {
147
+ $result = $mbstring->substr($result, 0, $pos);
148
+ }
149
+ }
150
+ elseif ($markUrl) {
151
  $result = $this->markLayeredNavigationUrl($result, '*/*/*', $params);
152
  }
153
  return $result;
186
  }
187
  $select->setPart(Zend_Db_Select::WHERE, $where);
188
  }
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  /**
191
  * @param Mana_Filters_Model_Filter2_Store $filterOptions
202
  throw new Exception('Not implemented');
203
  }
204
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  public function canShowFilterInBlock($block, $filter) {
206
  if ($block->getData('show_'.$filter->getCode())) {
207
  return true;
209
  elseif ($block->getData('hide_' . $filter->getCode())) {
210
  return false;
211
  }
212
+ elseif ($block->getData('show_all_filters')) {
213
+ return true;
214
+ }
215
+ elseif ($block->getData('hide_all_filters')) {
216
+ return false;
217
+ }
218
  elseif ($showInFilter = $block->getShowInFilter()) {
219
  $showIn = $filter->getShowIn();
220
  if (!is_array($showIn)) {
383
  return $result;
384
  }
385
 
386
+ public function isTreeVisible() {
387
+ /* @var $core Mana_Core_Helper_Data */
388
+ $core = Mage::helper('mana_core');
 
 
 
389
 
390
+ if ($core->isManadevLayeredNavigationTreeInstalled()) {
391
+ $filterCollection = $this->getFilterOptionsCollection(true);
392
+ foreach ($filterCollection as $filter) {
393
+ /* @var $filter Mana_Filters_Model_Filter2_Store */
394
+ if ($filter->getType() == 'category') {
395
+ if ($filter->getData('display') == 'tree') {
396
+ return true;
397
+ }
398
+ }
399
+ }
400
+ }
401
+ return false;
402
  }
 
 
 
 
 
 
403
 
404
+ #region Dependencies
 
405
 
406
+ /**
407
+ * @return Mana_Core_Helper_Data
408
+ */
409
+ public function coreHelper() {
410
+ return Mage::helper('mana_core');
 
 
 
 
411
  }
412
+ #endregion
413
  }
app/code/local/Mana/Filters/Model/Filter/Attribute.php CHANGED
@@ -9,6 +9,8 @@
9
  * Model type for holding information in memory about possible or applied filter which is based on an attribute
10
  * @author Mana Team
11
  * Injected instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_Filter_Attribute constructor.
 
 
12
  */
13
  class Mana_Filters_Model_Filter_Attribute
14
  extends Mage_Catalog_Model_Layer_Filter_Attribute
@@ -104,12 +106,13 @@ class Mana_Filters_Model_Filter_Attribute
104
  );
105
 
106
  $tags = $this->getLayer()->getStateTags($tags);
107
- if ($sortMethod = $this->getFilterOptions()->getSortMethod()) {
108
- foreach ($data as $position => &$item) {
109
- $item['position'] = $position;
110
- }
111
- usort($data, array(Mage::getSingleton('mana_filters/sort'), $sortMethod));
112
  }
 
 
113
  $this->getLayer()->getAggregator()->saveCacheData($data, $key, $tags);
114
  }
115
 
9
  * Model type for holding information in memory about possible or applied filter which is based on an attribute
10
  * @author Mana Team
11
  * Injected instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_Filter_Attribute constructor.
12
+ *
13
+ * @method Mana_Filters_Model_Filter2_Store getFilterOptions()
14
  */
15
  class Mana_Filters_Model_Filter_Attribute
16
  extends Mage_Catalog_Model_Layer_Filter_Attribute
106
  );
107
 
108
  $tags = $this->getLayer()->getStateTags($tags);
109
+
110
+ $sortMethod = $this->getFilterOptions()->getSortMethod() ? $this->getFilterOptions()->getSortMethod() : 'byPosition';
111
+ foreach ($data as $position => &$item) {
112
+ $item['position'] = $position;
 
113
  }
114
+ usort($data, array(Mage::getSingleton('mana_filters/sort'), $sortMethod));
115
+
116
  $this->getLayer()->getAggregator()->saveCacheData($data, $key, $tags);
117
  }
118
 
app/code/local/Mana/Filters/Model/Filter/Category.php CHANGED
@@ -9,6 +9,7 @@
9
  * Model type for holding information in memory about possible or applied category filter
10
  * @author Mana Team
11
  * Injected instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_Filter_Category constructor.
 
12
  */
13
  class Mana_Filters_Model_Filter_Category
14
  extends Mage_Catalog_Model_Layer_Filter_Category
@@ -58,7 +59,8 @@ class Mana_Filters_Model_Filter_Category
58
  {
59
  $data = array();
60
  foreach ($categories as $category) {
61
- if ($category->getIsActive() && $category->getProductCount()) {
 
62
  $data[] = array(
63
  'label' => Mage::helper('core')->htmlEscape($category->getName()),
64
  'value' => $category->getId(),
@@ -314,4 +316,29 @@ class Mana_Filters_Model_Filter_Category
314
  }
315
  #endregion
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  }
9
  * Model type for holding information in memory about possible or applied category filter
10
  * @author Mana Team
11
  * Injected instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_Filter_Category constructor.
12
+ * @method Mana_Filters_Model_Filter2_Store getFilterOptions()
13
  */
14
  class Mana_Filters_Model_Filter_Category
15
  extends Mage_Catalog_Model_Layer_Filter_Category
59
  {
60
  $data = array();
61
  foreach ($categories as $category) {
62
+ if ($category->getIsActive() &&
63
+ ($this->filterHelper()->isFilterEnabled($this->getFilterOptions()) == 2 || $category->getProductCount())) {
64
  $data[] = array(
65
  'label' => Mage::helper('core')->htmlEscape($category->getName()),
66
  'value' => $category->getId(),
316
  }
317
  #endregion
318
 
319
+ public function getResetValue() {
320
+ if ($this->_appliedCategory) {
321
+ /**
322
+ * Revert path ids
323
+ */
324
+ $pathIds = array_reverse($this->_appliedCategory->getPathIds());
325
+ $curCategoryId = $this->getLayer()->getCurrentCategory()->getId();
326
+
327
+ if ($pathIds[0] != $curCategoryId && in_array($curCategoryId, $pathIds) && isset($pathIds[1]) && $pathIds[1] != $curCategoryId) {
328
+ return $pathIds[1];
329
+ }
330
+ }
331
+
332
+ return null;
333
+ }
334
+
335
+ #region Dependencies
336
+
337
+ /**
338
+ * @return Mana_Filters_Helper_Data
339
+ */
340
+ public function filterHelper() {
341
+ return Mage::helper('mana_filters');
342
+ }
343
+ #endregion
344
  }
app/code/local/Mana/Filters/Model/Filter/Decimal.php CHANGED
@@ -30,8 +30,8 @@ class Mana_Filters_Model_Filter_Decimal
30
  return $result->getLabel();
31
  } else {
32
  $store = Mage::app()->getStore();
33
- $fromPrice = $store->formatPrice($range['from']);
34
- $toPrice = $store->formatPrice($range['to']);
35
 
36
  return Mage::helper('catalog')->__('%s - %s', $fromPrice, $toPrice);
37
  }
@@ -55,8 +55,10 @@ class Mana_Filters_Model_Filter_Decimal
55
  if ($this->_getIsFilterable() == 2) {
56
  $nonEmptyRanges = $dbRanges;
57
  $dbRanges = array();
58
- for ($i = 1; ($i + 1) * $range > $this->getMinValue() && ($i - 1) * $range < $this->getMaxValue(); $i++) {
59
- $dbRanges[$i] = isset($nonEmptyRanges[$i]) ? $nonEmptyRanges[$i] : 0;
 
 
60
  }
61
  }
62
  $data = array();
@@ -305,7 +307,7 @@ class Mana_Filters_Model_Filter_Decimal
305
  $range = $this->getData('range');
306
 
307
  $value = $this->getMSelectedValues();
308
- if (!empty($value)) {
309
  list($index, $range) = explode(',', $value[0]);
310
  }
311
 
@@ -392,9 +394,11 @@ class Mana_Filters_Model_Filter_Decimal
392
  * Returns all values currently selected for this filter
393
  */
394
  public function getMSelectedValues() {
395
- $values = Mage::helper('mana_core')->sanitizeRequestNumberParam($this->_requestVar,
396
- array(array('sep' => '_', 'as_string' => true), array('sep' => ',', 'as_string' => true)));
397
- return $values ? array_filter(explode('_', $values)) : array();
 
 
398
  }
399
 
400
  /**
@@ -416,17 +420,19 @@ class Mana_Filters_Model_Filter_Decimal
416
  public function addToState()
417
  {
418
  foreach ($this->getMSelectedValues() as $selection) {
419
- list($index, $range) = explode(',', $selection);
420
- $this->getLayer()->getState()->addFilter(
421
- $this->_createItemEx(
422
- array(
423
- 'label' => $this->_renderItemLabel($range, $index),
424
- 'value' => $selection,
425
- 'm_selected' => true,
426
- 'm_show_selected' => $this->getFilterOptions()->getIsReverse(),
 
 
427
  )
428
- )
429
- );
430
  }
431
  }
432
 
30
  return $result->getLabel();
31
  } else {
32
  $store = Mage::app()->getStore();
33
+ $fromPrice = $store->formatPrice($range['from'], false);
34
+ $toPrice = $store->formatPrice($range['to'], false);
35
 
36
  return Mage::helper('catalog')->__('%s - %s', $fromPrice, $toPrice);
37
  }
55
  if ($this->_getIsFilterable() == 2) {
56
  $nonEmptyRanges = $dbRanges;
57
  $dbRanges = array();
58
+ $from = (int)floor($this->getMinValue() / $range);
59
+ $to = (int)floor($this->getMaxValue() / $range);
60
+ for ($i = $from; $i <= $to; $i++) {
61
+ $dbRanges[$i] = isset($nonEmptyRanges[$i]) ? $nonEmptyRanges[$i] : 0;
62
  }
63
  }
64
  $data = array();
307
  $range = $this->getData('range');
308
 
309
  $value = $this->getMSelectedValues();
310
+ if (!empty($value) && strpos($value[0], ',') !== false) {
311
  list($index, $range) = explode(',', $value[0]);
312
  }
313
 
394
  * Returns all values currently selected for this filter
395
  */
396
  public function getMSelectedValues() {
397
+ $values = Mage::helper('mana_core')->sanitizeRequestNumberParam($this->_requestVar, array(
398
+ array('sep' => '_', 'as_string' => true),
399
+ array('sep' => ',', 'as_string' => true)
400
+ ));
401
+ return $values ? explode('_', $values) : array();
402
  }
403
 
404
  /**
420
  public function addToState()
421
  {
422
  foreach ($this->getMSelectedValues() as $selection) {
423
+ if (strpos($selection, ',') !== false) {
424
+ list($index, $range) = explode(',', $selection);
425
+ $this->getLayer()->getState()->addFilter(
426
+ $this->_createItemEx(
427
+ array(
428
+ 'label' => $this->_renderItemLabel($range, $index),
429
+ 'value' => $selection,
430
+ 'm_selected' => true,
431
+ 'm_show_selected' => $this->getFilterOptions()->getIsReverse(),
432
+ )
433
  )
434
+ );
435
+ }
436
  }
437
  }
438
 
app/code/local/Mana/Filters/Model/Filter/Price.php CHANGED
@@ -110,10 +110,16 @@ class Mana_Filters_Model_Filter_Price
110
  ->setStoreId(Mage::app()->getStore()->getId())
111
  ->load(Mage::app()->getStore()->getRootCategoryId());
112
  $currentCategory = $this->getLayer()->getCurrentCategory();
113
- $this->getLayer()->setCurrentCategory($rootCategory);
114
- $queryResult = $query->getFilterRange($this->getFilterOptions()->getCode(), false,
115
- $this->getLayer()->getProductCollection(), false);
116
- $this->getLayer()->setCurrentCategory($currentCategory);
 
 
 
 
 
 
117
  $this->_maxPriceInt = $queryResult['max'];
118
  $this->_hasNoResults = true;
119
  }
@@ -310,66 +316,70 @@ class Mana_Filters_Model_Filter_Price
310
  }
311
 
312
  public function getPriceRange() {
313
- $range = $this->getData('price_range');
314
-
315
- $value = $this->getMSelectedValues();
316
- if (!empty($value)) {
317
- list($index, $range) = explode(',', $value[0]);
318
- }
319
 
320
- if (!$range) {
321
- if (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24)) {
322
- $range = (float)$this->getFilterOptions()->getRangeStep();
323
- }
324
- elseif (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24, 'global_default_mask')) {
325
- $range = (float)$this->getFilterOptions()->getGlobalRangeStep();
326
- }
327
- }
328
- if (!$range) {
329
- $currentCategory = Mage::registry('current_category_filter');
330
- if ($currentCategory) {
331
- $range = $currentCategory->getFilterPriceRange();
332
- } else {
333
- $range = $this->getLayer()->getCurrentCategory()->getFilterPriceRange();
334
  }
335
 
336
- $maxPrice = $this->getMaxPriceInt();
337
  if (!$range) {
338
- $calculation = Mage::app()->getStore()->getConfig('catalog/layered_navigation/price_range_calculation');
339
- if (!$calculation) {
340
- $calculation = 'auto';
341
  }
342
- if ($calculation == 'auto') {
343
- if ($this->hasNoResults()) {
344
- $range = 1;
345
- while (ceil($maxPrice / $range) > 10) {
346
- $range *= 10;
347
- }
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
- else {
350
- $index = 1;
351
- do {
352
- $range = pow(10, (strlen(floor($maxPrice)) - $index));
353
- $this->setData('price_range', $range);
354
- /* @var $query Mana_Filters_Model_Query */
355
- $query = $this->getQuery();
356
- $items = $query->getFilterCounts($this->getFilterOptions()->getCode(), false);
357
- $index++;
358
  }
359
- while($range > self::MIN_RANGE_POWER && count($items) < 2);
 
 
 
 
 
 
 
 
 
360
 
361
 
362
- while (ceil($maxPrice / $range) > 25) {
363
- $range *= 10;
 
364
  }
365
  }
366
- } else {
367
- $range = Mage::app()->getStore()->getConfig('catalog/layered_navigation/price_range_step');
 
368
  }
369
- }
370
 
 
 
371
  }
372
- $this->setData('price_range', $range);
373
  return $this->getData('price_range');
374
  }
375
  public function init() {
@@ -378,9 +388,11 @@ class Mana_Filters_Model_Filter_Price
378
  * Returns all values currently selected for this filter
379
  */
380
  public function getMSelectedValues() {
381
- $values = Mage::helper('mana_core')->sanitizeRequestNumberParam($this->_requestVar,
382
- array(array('sep' => '_', 'as_string' => true), array('sep' => ',', 'as_string' => true)));
383
- return $values ? array_filter(explode('_', $values)) : array();
 
 
384
  }
385
 
386
  /**
@@ -391,17 +403,19 @@ class Mana_Filters_Model_Filter_Price
391
  public function addToState()
392
  {
393
  foreach ($this->getMSelectedValues() as $selection) {
394
- list($index, $range) = explode(',', $selection);
395
- $this->getLayer()->getState()->addFilter(
396
- $this->_createItemEx(
397
- array(
398
- 'label' => $this->_renderItemLabel($range, $index),
399
- 'value' => $selection,
400
- 'm_selected' => true,
401
- 'm_show_selected' => $this->getFilterOptions()->getIsReverse(),
 
 
402
  )
403
- )
404
- );
405
  }
406
  }
407
 
110
  ->setStoreId(Mage::app()->getStore()->getId())
111
  ->load(Mage::app()->getStore()->getRootCategoryId());
112
  $currentCategory = $this->getLayer()->getCurrentCategory();
113
+ if ($rootCategory->getId() != $currentCategory->getId()) {
114
+ $this->getLayer()->setCurrentCategory($rootCategory);
115
+ $queryResult = $query->getFilterRange($this->getFilterOptions()->getCode(), false,
116
+ $this->getLayer()->getProductCollection(), false);
117
+ $this->getLayer()->setCurrentCategory($currentCategory);
118
+ }
119
+ else {
120
+ $queryResult = $query->getFilterRange($this->getFilterOptions()->getCode(), false,
121
+ $query->createProductCollection(), false);
122
+ }
123
  $this->_maxPriceInt = $queryResult['max'];
124
  $this->_hasNoResults = true;
125
  }
316
  }
317
 
318
  public function getPriceRange() {
319
+ if (!$this->getData('price_range_set')) {
320
+ $this->setData('price_range_set', true);
321
+ $range = $this->getData('price_range');
 
 
 
322
 
323
+ $value = $this->getMSelectedValues();
324
+ if (!empty($value) && strpos($value[0], ',') !== false) {
325
+ list($index, $range) = explode(',', $value[0]);
 
 
 
 
 
 
 
 
 
 
 
326
  }
327
 
 
328
  if (!$range) {
329
+ if (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24)) {
330
+ $range = (float)$this->getFilterOptions()->getRangeStep();
 
331
  }
332
+ elseif (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24, 'global_default_mask')) {
333
+ $range = (float)$this->getFilterOptions()->getGlobalRangeStep();
334
+ }
335
+ }
336
+ if (!$range) {
337
+ $currentCategory = Mage::registry('current_category_filter');
338
+ if ($currentCategory) {
339
+ $range = $currentCategory->getFilterPriceRange();
340
+ }
341
+ else {
342
+ $range = $this->getLayer()->getCurrentCategory()->getFilterPriceRange();
343
+ }
344
+
345
+ $maxPrice = $this->getMaxPriceInt();
346
+ if (!$range) {
347
+ $calculation = Mage::app()->getStore()->getConfig('catalog/layered_navigation/price_range_calculation');
348
+ if (!$calculation) {
349
+ $calculation = 'auto';
350
  }
351
+ if ($calculation == 'auto') {
352
+ if ($this->hasNoResults()) {
353
+ $range = 1;
354
+ while (ceil($maxPrice / $range) > 10) {
355
+ $range *= 10;
356
+ }
 
 
 
357
  }
358
+ else {
359
+ $index = 1;
360
+ do {
361
+ $range = pow(10, (strlen(floor($maxPrice)) - $index));
362
+ $this->setData('price_range', $range);
363
+ /* @var $query Mana_Filters_Model_Query */
364
+ $query = $this->getQuery();
365
+ $items = $query->getFilterCounts($this->getFilterOptions()->getCode(), false);
366
+ $index++;
367
+ } while ($range > self::MIN_RANGE_POWER && count($items) < 2);
368
 
369
 
370
+ while (ceil($maxPrice / $range) > 25) {
371
+ $range *= 10;
372
+ }
373
  }
374
  }
375
+ else {
376
+ $range = Mage::app()->getStore()->getConfig('catalog/layered_navigation/price_range_step');
377
+ }
378
  }
 
379
 
380
+ }
381
+ $this->setData('price_range', $range);
382
  }
 
383
  return $this->getData('price_range');
384
  }
385
  public function init() {
388
  * Returns all values currently selected for this filter
389
  */
390
  public function getMSelectedValues() {
391
+ $values = Mage::helper('mana_core')->sanitizeRequestNumberParam($this->_requestVar, array(
392
+ array('sep' => '_', 'as_string' => true),
393
+ array('sep' => ',', 'as_string' => true)
394
+ ));
395
+ return $values ? explode('_', $values) : array();
396
  }
397
 
398
  /**
403
  public function addToState()
404
  {
405
  foreach ($this->getMSelectedValues() as $selection) {
406
+ if (strpos($selection, ',') !== false) {
407
+ list($index, $range) = explode(',', $selection);
408
+ $this->getLayer()->getState()->addFilter(
409
+ $this->_createItemEx(
410
+ array(
411
+ 'label' => $this->_renderItemLabel($range, $index),
412
+ 'value' => $selection,
413
+ 'm_selected' => true,
414
+ 'm_show_selected' => $this->getFilterOptions()->getIsReverse(),
415
+ )
416
  )
417
+ );
418
+ }
419
  }
420
  }
421
 
app/code/local/Mana/Filters/Model/Filter2.php CHANGED
@@ -10,9 +10,11 @@
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
  protected $_eventPrefix = 'mana_filter';
 
16
 
17
  /**
18
  * Invoked during model creation process, this method associates this model with resource and resource
@@ -58,4 +60,27 @@ class Mana_Filters_Model_Filter2 extends Mana_Db_Model_Object {
58
  public function getCode() {
59
  return isset($this->_data['code']) ? $this->_data['code'] : null;
60
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
10
  /**
11
  * INSERT HERE: what is this model for
12
  * @author Mana Team
13
+ * @method string getType()
14
  */
15
  class Mana_Filters_Model_Filter2 extends Mana_Db_Model_Object {
16
  protected $_eventPrefix = 'mana_filter';
17
+ protected $_entity = 'mana_filters/filter2';
18
 
19
  /**
20
  * Invoked during model creation process, this method associates this model with resource and resource
60
  public function getCode() {
61
  return isset($this->_data['code']) ? $this->_data['code'] : null;
62
  }
63
+
64
+ /**
65
+ * Init indexing process after category data commit
66
+ *
67
+ * @return Mage_Catalog_Model_Category
68
+ */
69
+ public function afterCommitCallback() {
70
+ parent::afterCommitCallback();
71
+ if (!Mage::registry('m_prevent_indexing_on_save')) {
72
+ $this->getIndexerSingleton()->processEntityAction($this, $this->_entity, Mage_Index_Model_Event::TYPE_SAVE);
73
+ }
74
+
75
+ return $this;
76
+ }
77
+
78
+ #region Dependencies
79
+ /**
80
+ * @return Mage_Index_Model_Indexer
81
+ */
82
+ public function getIndexerSingleton() {
83
+ return Mage::getSingleton('index/indexer');
84
+ }
85
+ #endregion
86
  }
app/code/local/Mana/Filters/Model/Filter2/Store.php CHANGED
@@ -13,6 +13,8 @@
13
  */
14
  class Mana_Filters_Model_Filter2_Store extends Mana_Filters_Model_Filter2 {
15
  protected $_eventPrefix = 'mana_filter_store';
 
 
16
  /**
17
  * Invoked during model creation process, this method associates this model with resource and resource
18
  * collection classes
@@ -30,7 +32,8 @@ class Mana_Filters_Model_Filter2_Store extends Mana_Filters_Model_Filter2 {
30
  $result = $this->getData($field);
31
  $store = Mage::app()->getStore();
32
  if ($result == '$0') {
33
- return $store->getCurrentCurrency()->formatPrecision(0, 0, array(), false, false);
 
34
  }
35
  else {
36
  return $result;
13
  */
14
  class Mana_Filters_Model_Filter2_Store extends Mana_Filters_Model_Filter2 {
15
  protected $_eventPrefix = 'mana_filter_store';
16
+ protected $_entity = 'mana_filters/filter2_store';
17
+
18
  /**
19
  * Invoked during model creation process, this method associates this model with resource and resource
20
  * collection classes
32
  $result = $this->getData($field);
33
  $store = Mage::app()->getStore();
34
  if ($result == '$0') {
35
+ return str_replace('.00', '', str_replace(',00', '',
36
+ $store->getCurrentCurrency()->formatPrecision(0, 0, array(), false, false)));
37
  }
38
  else {
39
  return $result;
app/code/local/Mana/Filters/Model/Filter2/Value.php CHANGED
@@ -20,4 +20,15 @@ class Mana_Filters_Model_Filter2_Value extends Mana_Db_Model_Object {
20
  protected function _construct() {
21
  $this->_init(strtolower('Mana_Filters/Filter2_Value'));
22
  }
 
 
 
 
 
 
 
 
 
 
 
23
  }
20
  protected function _construct() {
21
  $this->_init(strtolower('Mana_Filters/Filter2_Value'));
22
  }
23
+
24
+ public function loadByFilterPosition($filterId, $position) {
25
+ /* @var $resource Mana_Filters_Resource_Filter2_Value */
26
+ $resource = $this->_getResource();
27
+ $resource->loadByFilterPosition($this, $filterId, $position);
28
+ $this->_afterLoad();
29
+ $this->setOrigData();
30
+ $this->_hasDataChanges = false;
31
+
32
+ return $this;
33
+ }
34
  }
app/code/local/Mana/Filters/Model/Item.php CHANGED
@@ -14,6 +14,8 @@
14
  * method.
15
  */
16
  class Mana_Filters_Model_Item extends Mage_Catalog_Model_Layer_Filter_Item {
 
 
17
  /**
18
  * Returns URL which should be loaded if person chooses to add this filter item into active filters
19
  * @return string
@@ -101,14 +103,42 @@ class Mana_Filters_Model_Item extends Mage_Catalog_Model_Layer_Filter_Item {
101
  return 'filter_'.$helper->getFilterName($block, $this->getFilter()).'_'.$this->getValue();
102
  }
103
 
104
- public function getSeoValue() {
105
- $urlValue = $this->getValue();
106
- /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
107
- if (Mage::app()->getRequest()->getParam('m-seo-enabled', true) && ((string)Mage::getConfig()->getNode('modules/ManaPro_FilterSeoLinks/active')) == 'true') {
108
- $url = Mage::getModel('manapro_filterseolinks/url');
109
- /* @var $url ManaPro_FilterSeoLinks_Model_Url */
110
- $urlValue = $url->encodeValue($this->getFilter()->getRequestVar(), $urlValue);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  }
112
- return $urlValue;
 
113
  }
114
  }
14
  * method.
15
  */
16
  class Mana_Filters_Model_Item extends Mage_Catalog_Model_Layer_Filter_Item {
17
+ protected $_seoData;
18
+
19
  /**
20
  * Returns URL which should be loaded if person chooses to add this filter item into active filters
21
  * @return string
103
  return 'filter_'.$helper->getFilterName($block, $this->getFilter()).'_'.$this->getValue();
104
  }
105
 
106
+ /**
107
+ * @return string
108
+ */
109
+ public function getSeoValue() {
110
+ return $this->getSeoData('url');
111
+ }
112
+
113
+ public function getSeoPrefix()
114
+ {
115
+ return $this->getSeoData('prefix');
116
+ }
117
+
118
+ public function getSeoPosition()
119
+ {
120
+ return $this->getSeoData('position');
121
+ }
122
+
123
+ public function getSeoData($key = false) {
124
+ if (!$this->_seoData) {
125
+ if (Mage::app()->getRequest()->getParam('m-seo-enabled', true) &&
126
+ ((string)Mage::getConfig()->getNode('modules/ManaPro_FilterSeoLinks/active')) == 'true'
127
+ ) {
128
+ /* @var $url Mana_Seo_Rewrite_Url */
129
+ $url = Mage::getModel('core/url');
130
+ $this->_seoData = $url->getItemData($this->getFilter()->getRequestVar(), $this->getValue());
131
+ }
132
+ else {
133
+ $this->_seoData = array(
134
+ 'url' => $this->getValue(),
135
+ 'prefix' => '',
136
+ 'position' => 0,
137
+ 'id' => 0,
138
+ );
139
+ }
140
  }
141
+
142
+ return $key !== false ? $this->_seoData[$key] : $this->_seoData;
143
  }
144
  }
app/code/local/Mana/Filters/Model/Observer.php CHANGED
@@ -29,6 +29,7 @@ class Mana_Filters_Model_Observer {
29
  'mana_filters/display/category',
30
  'mana_filters/display/decimal',
31
  'mana_filters/display/sort_method',
 
32
  ));
33
  }
34
  /**
@@ -50,7 +51,11 @@ class Mana_Filters_Model_Observer {
50
  * @param Varien_Event_Observer $observer
51
  */
52
  public function fixAttributeIndexerSelectForConfigurableProductDefaultValues($observer) {
53
- /* @var $select Varien_Db_Select */ $select = $observer->getEvent()->getSelect();
 
 
 
 
54
  /* @var $entityField Zend_Db_Expr */ $entityField = $observer->getEvent()->getEntityField();
55
 
56
  /* @var $res Mage_Core_Model_Resource */ $res = Mage::getSingleton('core/resource');
@@ -69,4 +74,43 @@ class Mana_Filters_Model_Observer {
69
  ->where("NOT((m_configurable_product.type_id = 'configurable') AND (m_configurable_attribute.is_configurable = 1))");
70
  }
71
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
29
  'mana_filters/display/category',
30
  'mana_filters/display/decimal',
31
  'mana_filters/display/sort_method',
32
+ 'mana_filters/display/disable_no_result_options',
33
  ));
34
  }
35
  /**
51
  * @param Varien_Event_Observer $observer
52
  */
53
  public function fixAttributeIndexerSelectForConfigurableProductDefaultValues($observer) {
54
+ if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
55
+ return;
56
+ }
57
+
58
+ /* @var $select Varien_Db_Select */ $select = $observer->getEvent()->getSelect();
59
  /* @var $entityField Zend_Db_Expr */ $entityField = $observer->getEvent()->getEntityField();
60
 
61
  /* @var $res Mage_Core_Model_Resource */ $res = Mage::getSingleton('core/resource');
74
  ->where("NOT((m_configurable_product.type_id = 'configurable') AND (m_configurable_attribute.is_configurable = 1))");
75
  }
76
  }
77
+
78
+ /**
79
+ * REPLACE THIS WITH DESCRIPTION (handles event "core_block_abstract_to_html_after")
80
+ * @param Varien_Event_Observer $observer
81
+ */
82
+ public function hideCmsContentOnFilteredPages($observer) {
83
+ /* @var $block Mage_Core_Block_Abstract */
84
+ $block = $observer->getEvent()->getBlock();
85
+ /* @var $transport Varien_Object */
86
+ $transport = $observer->getEvent()->getData('transport');
87
+
88
+ if ($block->getNameInLayout() == 'cms_page' &&
89
+ ($block->getData('hide_cms_content_when_filters_applied') || Mage::getStoreConfigFlag('mana_filters/display/hide_cms_page_content')) &&
90
+ in_array($this->coreHelper()->getRoutePath(), array('cms/page/view', 'cms/index/index')) &&
91
+ ($state = $this->filterHelper()->getLayer()->getState()) &&
92
+ $state->getFilters())
93
+
94
+ {
95
+ $transport->setData('html', '');
96
+ }
97
+ // INSERT HERE: event handler code
98
+ }
99
+
100
+ #region Dependencies
101
+
102
+ /**
103
+ * @return Mana_Core_Helper_Data
104
+ */
105
+ public function coreHelper() {
106
+ return Mage::helper('mana_core');
107
+ }
108
+
109
+ /**
110
+ * @return Mana_Filters_Helper_Data
111
+ */
112
+ public function filterHelper() {
113
+ return Mage::helper('mana_filters');
114
+ }
115
+ #endregion
116
  }
app/code/local/Mana/Filters/Model/Query.php CHANGED
@@ -160,6 +160,7 @@ class Mana_Filters_Model_Query extends Varien_Object
160
  $filterModel->applyToCollection($collection);
161
  }
162
  }
 
163
  $counts = $currentFilterModel->countOnCollection($collection);
164
  $currentFilter['processedCounts'] = $currentFilterModel->processCounts($counts);
165
 
160
  $filterModel->applyToCollection($collection);
161
  }
162
  }
163
+
164
  $counts = $currentFilterModel->countOnCollection($collection);
165
  $currentFilter['processedCounts'] = $currentFilterModel->processCounts($counts);
166
 
app/code/local/Mana/Filters/Model/Solr/Attribute.php CHANGED
@@ -19,11 +19,40 @@ class Mana_Filters_Model_Solr_Attribute extends Mana_Filters_Model_Filter_Attrib
19
  /* @var $collection Enterprise_Search_Model_Resource_Collection */
20
  $collection = $counts;
21
 
22
- $attribute = $this->getAttributeModel();
23
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
24
- $fieldName = $engine->getSearchEngineFieldName($attribute, 'nav');
25
- return $collection->getFacetedData($fieldName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
 
29
  /**
@@ -32,9 +61,7 @@ class Mana_Filters_Model_Solr_Attribute extends Mana_Filters_Model_Filter_Attrib
32
  */
33
  public function countOnCollection($collection)
34
  {
35
- $engine = Mage::getResourceSingleton('enterprise_search/engine');
36
- $facetField = $engine->getSearchEngineFieldName($this->getAttributeModel(), 'nav');
37
- $collection->setFacetCondition($facetField);
38
 
39
  return $collection;
40
  }
@@ -44,9 +71,16 @@ class Mana_Filters_Model_Solr_Attribute extends Mana_Filters_Model_Filter_Attrib
44
  */
45
  public function applyToCollection($collection)
46
  {
 
47
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
48
- $facetField = $engine->getSearchEngineFieldName($this->getAttributeModel(), 'nav');
49
- $collection->addFqFilter(array($facetField => array('or' => $this->getMSelectedValues())));
 
 
 
 
 
 
50
  }
51
 
52
  }
19
  /* @var $collection Enterprise_Search_Model_Resource_Collection */
20
  $collection = $counts;
21
 
22
+ $result = $collection->getFacetedData($this->getFilterField(), array('fields' => array('id')));
23
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
24
+ if (method_exists($engine, 'getSearchEngineFieldName')) {
25
+ return $result;
26
+ }
27
+ else {
28
+ $attribute = $this->getAttributeModel();
29
+ $options = $attribute->getFrontend()->getSelectOptions();
30
+ $idResult = array();
31
+ foreach ($options as $option) {
32
+ if (!$option || is_array($option['value'])) {
33
+ continue;
34
+ }
35
+ if (isset($result[$option['label']])) {
36
+ $idResult[$option['value']] = $result[$option['label']];
37
+ }
38
+ }
39
+ return $idResult;
40
+ }
41
+ }
42
 
43
+ /**
44
+ * Get facet field name
45
+ *
46
+ * @return string
47
+ */
48
+ public function getFilterField() {
49
+ $engine = Mage::getResourceSingleton('enterprise_search/engine');
50
+ if (method_exists($engine, 'getSearchEngineFieldName')) {
51
+ return $engine->getSearchEngineFieldName($this->getAttributeModel(), 'nav');
52
+ }
53
+ else {
54
+ return Mage::helper('enterprise_search')->getAttributeSolrFieldName($this->getAttributeModel());
55
+ }
56
  }
57
 
58
  /**
61
  */
62
  public function countOnCollection($collection)
63
  {
64
+ $collection->setFacetCondition($this->getFilterField());
 
 
65
 
66
  return $collection;
67
  }
71
  */
72
  public function applyToCollection($collection)
73
  {
74
+ $values = $this->getMSelectedValues();
75
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
76
+ if (!method_exists($engine, 'getSearchEngineFieldName')) {
77
+ $labels = array();
78
+ foreach ($values as $value) {
79
+ $labels[] = $this->getAttributeModel()->getFrontend()->getOption($value);
80
+ }
81
+ $values = $labels;
82
+ }
83
+ $collection->addFqFilter(array($this->getFilterField() => array('or' => $values)));
84
  }
85
 
86
  }
app/code/local/Mana/Filters/Model/Solr/Category.php CHANGED
@@ -15,11 +15,26 @@ class Mana_Filters_Model_Solr_Category extends Mana_Filters_Model_Filter_Categor
15
  return true;
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  public function processCounts($counts) {
19
  /* @var $collection Enterprise_Search_Model_Resource_Collection */
20
  $collection = $counts;
21
 
22
- $facetedData = $collection->getFacetedData('category_ids');
23
  foreach ($this->getCountedCategories() as $category) {
24
  if (isset($facetedData[$category->getId()])) {
25
  $category->setProductCount($facetedData[$category->getId()]);
@@ -42,7 +57,7 @@ class Mana_Filters_Model_Solr_Category extends Mana_Filters_Model_Filter_Categor
42
  ? array_keys($this->getCountedCategories())
43
  : array_keys($this->getCountedCategories()->toArray()));
44
 
45
- $collection->setFacetCondition('category_ids', $categories);
46
 
47
  return $collection;
48
  }
@@ -52,7 +67,7 @@ class Mana_Filters_Model_Solr_Category extends Mana_Filters_Model_Filter_Categor
52
  */
53
  public function applyToCollection($collection)
54
  {
55
- $collection->addFqFilter(array('category_ids' => array('or' => $this->getMSelectedValues())));
56
  }
57
 
58
  public function isFilterAppliedWhenCounting($modelToBeApplied) {
15
  return true;
16
  }
17
 
18
+ /**
19
+ * Get facet field name based on current website and customer group
20
+ *
21
+ * @return string
22
+ */
23
+ protected function _getFilterField() {
24
+ $engine = Mage::getResourceSingleton('enterprise_search/engine');
25
+ if (method_exists($engine, 'getSearchEngineFieldName')) {
26
+ return 'category_ids';
27
+ }
28
+ else {
29
+ return 'categories';
30
+ }
31
+ }
32
+
33
  public function processCounts($counts) {
34
  /* @var $collection Enterprise_Search_Model_Resource_Collection */
35
  $collection = $counts;
36
 
37
+ $facetedData = $collection->getFacetedData($this->_getFilterField());
38
  foreach ($this->getCountedCategories() as $category) {
39
  if (isset($facetedData[$category->getId()])) {
40
  $category->setProductCount($facetedData[$category->getId()]);
57
  ? array_keys($this->getCountedCategories())
58
  : array_keys($this->getCountedCategories()->toArray()));
59
 
60
+ $collection->setFacetCondition($this->_getFilterField(), $categories);
61
 
62
  return $collection;
63
  }
67
  */
68
  public function applyToCollection($collection)
69
  {
70
+ $collection->addFqFilter(array($this->_getFilterField() => array('or' => $this->getMSelectedValues())));
71
  }
72
 
73
  public function isFilterAppliedWhenCounting($modelToBeApplied) {
app/code/local/Mana/Filters/Model/Solr/Decimal.php CHANGED
@@ -72,12 +72,14 @@ class Mana_Filters_Model_Solr_Decimal extends Mana_Filters_Model_Filter_Decimal
72
 
73
  $fq = array();
74
  foreach ($this->getMSelectedValues() as $selection) {
75
- list($index, $range) = explode(',', $selection);
76
- $range = $this->_getResource()->getRange($index, $range);
77
- $fq[] = array(
78
- 'from' => $range['from'],
79
- 'to' => $range['to'] - ($this->isUpperBoundInclusive() ? 0 : 0.001),
80
- );
 
 
81
  }
82
 
83
  $collection->addFqFilter(array($field => array('or' => $fq)));
72
 
73
  $fq = array();
74
  foreach ($this->getMSelectedValues() as $selection) {
75
+ if (strpos($selection, ',') !== false) {
76
+ list($index, $range) = explode(',', $selection);
77
+ $range = $this->_getResource()->getRange($index, $range);
78
+ $fq[] = array(
79
+ 'from' => $range['from'],
80
+ 'to' => $range['to'] - ($this->isUpperBoundInclusive() ? 0 : 0.001),
81
+ );
82
+ }
83
  }
84
 
85
  $collection->addFqFilter(array($field => array('or' => $fq)));
app/code/local/Mana/Filters/Model/Solr/Price.php CHANGED
@@ -26,7 +26,7 @@ class Mana_Filters_Model_Solr_Price extends Mana_Filters_Model_Filter_Price
26
  */
27
  public function countOnCollection($collection)
28
  {
29
- if (Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_CALCULATION) == self::RANGE_CALCULATION_IMPROVED) {
30
  return $this->_addCalculatedFacetConditionToCollection($collection);
31
  }
32
 
@@ -79,17 +79,19 @@ class Mana_Filters_Model_Solr_Price extends Mana_Filters_Model_Filter_Price
79
  $field = $this->_getFilterField();
80
  $fq = array();
81
  foreach ($this->getMSelectedValues() as $selection) {
82
- list($index, $range) = explode(',', $selection);
83
- $range = $this->_getResource()->getPriceRange($index, $range);
84
- $to = $range['to'];
85
- if ($to < $this->getMaxPriceInt() && !$this->isUpperBoundInclusive()) {
86
- $to -= 0.001;
87
- }
 
88
 
89
- $fq[] = array(
90
- 'from' => $range['from'],
91
- 'to' => $to,
92
- );
 
93
  }
94
 
95
  $collection->addFqFilter(array($field => array('or' => $fq)));
@@ -120,7 +122,14 @@ class Mana_Filters_Model_Solr_Price extends Mana_Filters_Model_Filter_Price
120
  protected function _getFilterField()
121
  {
122
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
123
- $priceField = $engine->getSearchEngineFieldName('price');
 
 
 
 
 
 
 
124
 
125
  return $priceField;
126
  }
@@ -274,7 +283,7 @@ class Mana_Filters_Model_Solr_Price extends Mana_Filters_Model_Filter_Price
274
 
275
  $max = $stats[$this->_getFilterField()]['max'];
276
  if (!is_numeric($max)) {
277
- $max = parent::getRangeOnCollection($collection);
278
  } else {
279
  $max = floor($max * $this->getCurrencyRate());
280
  }
26
  */
27
  public function countOnCollection($collection)
28
  {
29
+ if (Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_CALCULATION) == 'improved') {
30
  return $this->_addCalculatedFacetConditionToCollection($collection);
31
  }
32
 
79
  $field = $this->_getFilterField();
80
  $fq = array();
81
  foreach ($this->getMSelectedValues() as $selection) {
82
+ if (strpos($selection, ',') !== false) {
83
+ list($index, $range) = explode(',', $selection);
84
+ $range = $this->_getResource()->getPriceRange($index, $range);
85
+ $to = $range['to'];
86
+ if ($to < $this->getMaxPriceInt() && !$this->isUpperBoundInclusive()) {
87
+ $to -= 0.001;
88
+ }
89
 
90
+ $fq[] = array(
91
+ 'from' => $range['from'],
92
+ 'to' => $to,
93
+ );
94
+ }
95
  }
96
 
97
  $collection->addFqFilter(array($field => array('or' => $fq)));
122
  protected function _getFilterField()
123
  {
124
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
125
+ if (method_exists($engine, 'getSearchEngineFieldName')) {
126
+ $priceField = $engine->getSearchEngineFieldName('price');
127
+ }
128
+ else {
129
+ $websiteId = Mage::app()->getStore()->getWebsiteId();
130
+ $customerGroupId = Mage::getSingleton('customer/session')->getCustomerGroupId();
131
+ $priceField = 'price_' . $customerGroupId . '_' . $websiteId;
132
+ }
133
 
134
  return $priceField;
135
  }
283
 
284
  $max = $stats[$this->_getFilterField()]['max'];
285
  if (!is_numeric($max)) {
286
+ return parent::getRangeOnCollection($collection);
287
  } else {
288
  $max = floor($max * $this->getCurrencyRate());
289
  }
app/code/local/Mana/Filters/Model/Solr/Reverse/Attribute.php CHANGED
@@ -17,8 +17,7 @@ class Mana_Filters_Model_Solr_Reverse_Attribute extends Mana_Filters_Model_Solr_
17
  public function applyToCollection($collection)
18
  {
19
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
20
- $facetField = $engine->getSearchEngineFieldName($this->getAttributeModel(), 'nav');
21
- $collection->addFqFilter(array($facetField => array('reverse' => $this->getMSelectedValues())));
22
  }
23
 
24
  }
17
  public function applyToCollection($collection)
18
  {
19
  $engine = Mage::getResourceSingleton('enterprise_search/engine');
20
+ $collection->addFqFilter(array($this->getFilterField() => array('reverse' => $this->getMSelectedValues())));
 
21
  }
22
 
23
  }
app/code/local/Mana/Filters/Model/Solr/Reverse/Decimal.php CHANGED
@@ -21,11 +21,13 @@ class Mana_Filters_Model_Solr_Reverse_Decimal extends Mana_Filters_Model_Solr_De
21
 
22
  $fq = array();
23
  foreach ($this->getMSelectedValues() as $selection) {
24
- list($index, $range) = explode(',', $selection);
25
- $fq[] = array(
26
- 'from' => ($range * ($index - 1)),
27
- 'to' => $range * $index - ($this->isUpperBoundInclusive() ? 0 : 0.001),
28
- );
 
 
29
  }
30
 
31
  $collection->addFqFilter(array($field => array('reverse' => $fq)));
21
 
22
  $fq = array();
23
  foreach ($this->getMSelectedValues() as $selection) {
24
+ if (strpos($selection, ',') !== false) {
25
+ list($index, $range) = explode(',', $selection);
26
+ $fq[] = array(
27
+ 'from' => ($range * ($index - 1)),
28
+ 'to' => $range * $index - ($this->isUpperBoundInclusive() ? 0 : 0.001),
29
+ );
30
+ }
31
  }
32
 
33
  $collection->addFqFilter(array($field => array('reverse' => $fq)));
app/code/local/Mana/Filters/Model/Solr/Reverse/Price.php CHANGED
@@ -22,16 +22,18 @@ class Mana_Filters_Model_Solr_Reverse_Price extends Mana_Filters_Model_Solr_Pric
22
  $field = $this->_getFilterField();
23
  $fq = array();
24
  foreach ($this->getMSelectedValues() as $selection) {
25
- list($index, $range) = explode(',', $selection);
26
- $to = $range * $index;
27
- if ($to < $this->getMaxPriceInt() && !$this->isUpperBoundInclusive()) {
28
- $to -= 0.001;
29
- }
 
30
 
31
- $fq[] = array(
32
- 'from' => ($range * ($index - 1)),
33
- 'to' => $to,
34
- );
 
35
  }
36
 
37
  $collection->addFqFilter(array($field => array('reverse' => $fq)));
22
  $field = $this->_getFilterField();
23
  $fq = array();
24
  foreach ($this->getMSelectedValues() as $selection) {
25
+ if (strpos($selection, ',') !== false) {
26
+ list($index, $range) = explode(',', $selection);
27
+ $to = $range * $index;
28
+ if ($to < $this->getMaxPriceInt() && !$this->isUpperBoundInclusive()) {
29
+ $to -= 0.001;
30
+ }
31
 
32
+ $fq[] = array(
33
+ 'from' => ($range * ($index - 1)),
34
+ 'to' => $to,
35
+ );
36
+ }
37
  }
38
 
39
  $collection->addFqFilter(array($field => array('reverse' => $fq)));
app/code/local/Mana/Filters/Resource/Filter/Decimal.php CHANGED
@@ -62,25 +62,27 @@ class Mana_Filters_Resource_Filter_Decimal extends Mage_Catalog_Model_Resource_E
62
  $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId())
63
  );
64
 
65
- $collection->getSelect()->join(
66
- array($tableAlias => $this->getMainTable()),
67
- join(' AND ', $conditions),
68
- array()
69
- );
70
-
71
- // MANA BEGIN: modify select formation to include multiple price ranges
72
  $condition = '';
73
  foreach ($value as $selection) {
74
- list($index, $range) = explode(',', $selection);
75
- $range = $this->getRange($index, $range);
76
- if ($condition != '') $condition .= ' OR ';
77
- $condition .= '(('."{$tableAlias}.value" . ' >= '. $range['from'].') '.
78
- 'AND ('."{$tableAlias}.value" . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
- $collection->getSelect()
81
- ->distinct()
82
- ->where($condition);
83
- // MANA END
84
 
85
  return $this;
86
  }
@@ -122,7 +124,7 @@ class Mana_Filters_Resource_Filter_Decimal extends Mage_Catalog_Model_Resource_E
122
  */
123
  public function getMinMaxForCollection($filter, $collection)
124
  {
125
- $select = $this->_getSelect($filter, $collection);
126
  $connection = $this->_getReadAdapter();
127
 
128
  $table = 'decimal_index';
62
  $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId())
63
  );
64
 
 
 
 
 
 
 
 
65
  $condition = '';
66
  foreach ($value as $selection) {
67
+ if (strpos($selection, ',') !== false) {
68
+ list($index, $range) = explode(',', $selection);
69
+ $range = $this->getRange($index, $range);
70
+ if ($condition != '') $condition .= ' OR ';
71
+ $condition .= '(('."{$tableAlias}.value" . ' >= '. $range['from'].') '.
72
+ 'AND ('."{$tableAlias}.value" . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
73
+ }
74
+ }
75
+
76
+ if ($condition) {
77
+ $collection->getSelect()
78
+ ->join(
79
+ array($tableAlias => $this->getMainTable()),
80
+ join(' AND ', $conditions),
81
+ array()
82
+ )
83
+ ->distinct()
84
+ ->where($condition);
85
  }
 
 
 
 
86
 
87
  return $this;
88
  }
124
  */
125
  public function getMinMaxForCollection($filter, $collection)
126
  {
127
+ $select = $this->_getSelectForCollection($filter, $collection);
128
  $connection = $this->_getReadAdapter();
129
 
130
  $table = 'decimal_index';
app/code/local/Mana/Filters/Resource/Filter/Price.php CHANGED
@@ -25,14 +25,11 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
25
  {
26
  $collection->addPriceData($filter->getCustomerGroupId(), $filter->getWebsiteId());
27
 
28
- if (!is_null($collection->getCatalogPreparedSelect())) {
29
- $select = clone $collection->getCatalogPreparedSelect();
30
- } else {
31
- $select = clone $collection->getSelect();
32
- }
33
 
34
  // reset columns, order and limitation conditions
35
  $select->reset(Zend_Db_Select::COLUMNS);
 
36
  $select->reset(Zend_Db_Select::ORDER);
37
  $select->reset(Zend_Db_Select::LIMIT_COUNT);
38
  $select->reset(Zend_Db_Select::LIMIT_OFFSET);
@@ -100,15 +97,15 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
100
  if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
101
  $table = Mage_Catalog_Model_Resource_Product_Collection::MAIN_TABLE_ALIAS;
102
  $select = $this->_getSelectOnCollection($collection, $model);
103
- $priceExpression = $this->_getFullPriceExpression($model, $select);
 
104
 
105
  $range = floatval($model->getPriceRange());
106
  if ($range == 0) {
107
  $range = 1;
108
  }
109
- $fix = $this->_getConfigurablePriceFix();
110
  $countExpr = new Zend_Db_Expr('COUNT(*)');
111
- $rangeExpr = new Zend_Db_Expr("FLOOR(({$priceExpression} {$fix}) / {$range}) + 1");
112
 
113
  $select->columns(array(
114
  'range' => $rangeExpr,
@@ -119,6 +116,8 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
119
  Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
120
  $select->where("{$table}.min_price > 0");
121
 
 
 
122
  return $this->_getReadAdapter()->fetchPairs($select);
123
  }
124
  else {
@@ -174,15 +173,20 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
174
 
175
  $condition = '';
176
  foreach ($model->getMSelectedValues() as $selection) {
177
- list($index, $range) = explode(',', $selection);
178
- $range = $this->getPriceRange($index, $range);
179
- if ($condition != '') $condition .= ' OR ';
180
- $condition .= '(('.$priceExpr . ' >= '. $range['from'].') '.
181
- 'AND ('.$priceExpr . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
 
 
 
 
 
 
 
 
182
  }
183
- $select
184
- ->distinct()
185
- ->where($condition);
186
  return $this;
187
  }
188
 
@@ -201,21 +205,19 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
201
  {
202
  $select = $this->_getSelectOnCollection($collection, $filter);
203
  $connection = $this->_getReadAdapter();
204
- $response = $this->_dispatchPreparePriceEvent($filter, $select);
205
 
206
  if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
207
- $table = Mage_Catalog_Model_Resource_Product_Collection::MAIN_TABLE_ALIAS;
208
- $additional = $this->_replaceTableAlias(join('', $response->getAdditionalCalculations()));
209
  }
210
  else {
 
211
  $table = $this->_getIndexTableAlias();
212
  $additional = join('', $response->getAdditionalCalculations());
 
 
213
  }
214
 
215
- $fix = $this->_getConfigurablePriceFix();
216
- $maxPriceExpr = new Zend_Db_Expr("MAX({$table}.min_price {$additional} {$fix}) AS m_max_price");
217
-
218
- Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
219
  $select->columns(array($maxPriceExpr))->order('m_max_price DESC');
220
 
221
  $result = $connection->fetchOne($select) * $filter->getCurrencyRate();
@@ -255,4 +257,49 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
255
  }
256
  return $subSelect ? " + COALESCE(($subSelect), 0)" : '';
257
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
25
  {
26
  $collection->addPriceData($filter->getCustomerGroupId(), $filter->getWebsiteId());
27
 
28
+ $select = clone $collection->getSelect();
 
 
 
 
29
 
30
  // reset columns, order and limitation conditions
31
  $select->reset(Zend_Db_Select::COLUMNS);
32
+ $select->reset(Zend_Db_Select::GROUP);
33
  $select->reset(Zend_Db_Select::ORDER);
34
  $select->reset(Zend_Db_Select::LIMIT_COUNT);
35
  $select->reset(Zend_Db_Select::LIMIT_OFFSET);
97
  if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
98
  $table = Mage_Catalog_Model_Resource_Product_Collection::MAIN_TABLE_ALIAS;
99
  $select = $this->_getSelectOnCollection($collection, $model);
100
+ $rate = $model->getCurrencyRate();
101
+ $priceExpression = "({$this->_getPriceExpression($model, $select)}) * {$rate}";
102
 
103
  $range = floatval($model->getPriceRange());
104
  if ($range == 0) {
105
  $range = 1;
106
  }
 
107
  $countExpr = new Zend_Db_Expr('COUNT(*)');
108
+ $rangeExpr = new Zend_Db_Expr("FLOOR(({$priceExpression}) / {$range}) + 1");
109
 
110
  $select->columns(array(
111
  'range' => $rangeExpr,
116
  Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
117
  $select->where("{$table}.min_price > 0");
118
 
119
+ //Mage::log($select->__toString(), Zend_Log::DEBUG, 'price.log' );
120
+ //Mage::log(json_encode($this->_getReadAdapter()->fetchPairs($select)), Zend_Log::DEBUG, 'price.log' );
121
  return $this->_getReadAdapter()->fetchPairs($select);
122
  }
123
  else {
173
 
174
  $condition = '';
175
  foreach ($model->getMSelectedValues() as $selection) {
176
+ if (strpos($selection, ',') !== false) {
177
+ list($index, $range) = explode(',', $selection);
178
+ $range = $this->getPriceRange($index, $range);
179
+ if ($condition != '') $condition .= ' OR ';
180
+ $condition .= '(('.$priceExpr . ' >= '. $range['from'].') '.
181
+ 'AND ('.$priceExpr . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
182
+ }
183
+ }
184
+
185
+ if ($condition) {
186
+ $select
187
+ ->distinct()
188
+ ->where($condition);
189
  }
 
 
 
190
  return $this;
191
  }
192
 
205
  {
206
  $select = $this->_getSelectOnCollection($collection, $filter);
207
  $connection = $this->_getReadAdapter();
 
208
 
209
  if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
210
+ $maxPriceExpr = new Zend_Db_Expr("MAX({$this->_getPriceExpression($filter, $select)}) AS m_max_price");
 
211
  }
212
  else {
213
+ $response = $this->_dispatchPreparePriceEvent($filter, $select);
214
  $table = $this->_getIndexTableAlias();
215
  $additional = join('', $response->getAdditionalCalculations());
216
+ $fix = $this->_getConfigurablePriceFix();
217
+ $maxPriceExpr = new Zend_Db_Expr("MAX({$table}.min_price {$additional} {$fix}) AS m_max_price");
218
  }
219
 
220
+ //Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
 
 
 
221
  $select->columns(array($maxPriceExpr))->order('m_max_price DESC');
222
 
223
  $result = $connection->fetchOne($select) * $filter->getCurrencyRate();
257
  }
258
  return $subSelect ? " + COALESCE(($subSelect), 0)" : '';
259
  }
260
+
261
+ protected $_preparedExpressions = array();
262
+
263
+ protected function _getPriceExpression($filter, $select, $replaceAlias = true) {
264
+ foreach ($this->_preparedExpressions as $expr) {
265
+ if ($expr['select'] == $select) {
266
+ return $expr['result'];
267
+ }
268
+ }
269
+
270
+ $response = new Varien_Object();
271
+ $response->setAdditionalCalculations(array());
272
+ $tableAliases = array_keys($select->getPart(Zend_Db_Select::FROM));
273
+ if (in_array(Mage_Catalog_Model_Resource_Product_Collection::INDEX_TABLE_ALIAS, $tableAliases)) {
274
+ $table = Mage_Catalog_Model_Resource_Product_Collection::INDEX_TABLE_ALIAS;
275
+ }
276
+ else {
277
+ $table = reset($tableAliases);
278
+ }
279
+
280
+ // prepare event arguments
281
+ $eventArgs = array(
282
+ 'select' => $select,
283
+ 'table' => $table,
284
+ 'store_id' => $filter->getStoreId(),
285
+ 'response_object' => $response
286
+ );
287
+
288
+ Mage::dispatchEvent('catalog_prepare_price_select', $eventArgs);
289
+
290
+ $table = Mage_Catalog_Model_Resource_Product_Collection::MAIN_TABLE_ALIAS;
291
+ $additional = $this->_replaceTableAlias(join('', $response->getAdditionalCalculations()));
292
+
293
+ $fix = $this->_getConfigurablePriceFix();
294
+ $result = "{$table}.min_price {$additional} {$fix}";
295
+
296
+ if ($replaceAlias) {
297
+ $result = $this->_replaceTableAlias($result);
298
+ }
299
+
300
+ $this->_preparedExpressions[] = compact('select', 'result');
301
+
302
+ return $result;
303
+ }
304
+
305
  }
app/code/local/Mana/Filters/Resource/Filter/Reverse/Decimal.php CHANGED
@@ -28,25 +28,27 @@ class Mana_Filters_Resource_Filter_Reverse_Decimal extends Mana_Filters_Resource
28
  $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId())
29
  );
30
 
31
- $collection->getSelect()->join(
32
- array($tableAlias => $this->getMainTable()),
33
- join(' AND ', $conditions),
34
- array()
35
- );
36
-
37
- // MANA BEGIN: modify select formation to include multiple price ranges
38
  $condition = '';
39
  foreach ($value as $selection) {
40
- list($index, $range) = explode(',', $selection);
41
- $range = $this->getRange($index, $range);
42
- if ($condition != '') $condition .= ' OR ';
43
- $condition .= '(('."{$tableAlias}.value" . ' >= '. $range['from'].') '.
44
- 'AND ('."{$tableAlias}.value" . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
- $collection->getSelect()
47
- ->distinct()
48
- ->where("NOT ($condition)");
49
- // MANA END
50
 
51
  return $this;
52
  }
28
  $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId())
29
  );
30
 
 
 
 
 
 
 
 
31
  $condition = '';
32
  foreach ($value as $selection) {
33
+ if (strpos($selection, ',') !== false) {
34
+ list($index, $range) = explode(',', $selection);
35
+ $range = $this->getRange($index, $range);
36
+ if ($condition != '') $condition .= ' OR ';
37
+ $condition .= '(('."{$tableAlias}.value" . ' >= '. $range['from'].') '.
38
+ 'AND ('."{$tableAlias}.value" . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
39
+ }
40
+ }
41
+
42
+ if ($condition) {
43
+ $collection->getSelect()
44
+ ->join(
45
+ array($tableAlias => $this->getMainTable()),
46
+ join(' AND ', $conditions),
47
+ array()
48
+ )
49
+ ->distinct()
50
+ ->where("NOT ($condition)");
51
  }
 
 
 
 
52
 
53
  return $this;
54
  }
app/code/local/Mana/Filters/Resource/Filter/Reverse/Price.php CHANGED
@@ -38,15 +38,20 @@ class Mana_Filters_Resource_Filter_Reverse_Price extends Mana_Filters_Resource_F
38
 
39
  $condition = '';
40
  foreach ($model->getMSelectedValues() as $selection) {
41
- list($index, $range) = explode(',', $selection);
42
- $range = $this->getPriceRange($index, $range);
43
- if ($condition != '') $condition .= ' OR ';
44
- $condition .= '(('.$priceExpr . ' >= '. $range['from'].') '.
45
- 'AND ('.$priceExpr . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
 
 
 
 
 
 
 
 
46
  }
47
- $select
48
- ->distinct()
49
- ->where("NOT ($condition)");
50
  return $this;
51
  }
52
 
38
 
39
  $condition = '';
40
  foreach ($model->getMSelectedValues() as $selection) {
41
+ if (strpos($selection, ',') !== false) {
42
+ list($index, $range) = explode(',', $selection);
43
+ $range = $this->getPriceRange($index, $range);
44
+ if ($condition != '') $condition .= ' OR ';
45
+ $condition .= '(('.$priceExpr . ' >= '. $range['from'].') '.
46
+ 'AND ('.$priceExpr . ($this->isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
47
+ }
48
+ }
49
+
50
+ if ($condition) {
51
+ $select
52
+ ->distinct()
53
+ ->where("NOT ($condition)");
54
  }
 
 
 
55
  return $this;
56
  }
57
 
app/code/local/Mana/Filters/Resource/Filter2.php CHANGED
@@ -53,6 +53,12 @@ class Mana_Filters_Resource_Filter2 extends Mana_Db_Resource_Object {
53
  const DM_HELP_WIDTH = 33;
54
 
55
  const DM_IS_REVERSE = 34;
 
 
 
 
 
 
56
  #endregion
57
 
58
  /**
@@ -161,6 +167,9 @@ class Mana_Filters_Resource_Filter2 extends Mana_Db_Resource_Object {
161
  if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, Mana_Filters_Resource_Filter2::DM_SORT_METHOD)) {
162
  $object->setSortMethod(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/sort_method'));
163
  }
 
 
 
164
  }
165
 
166
  /**
@@ -238,6 +247,7 @@ class Mana_Filters_Resource_Filter2 extends Mana_Db_Resource_Object {
238
  $object->setIsEnabledInSearch($values['is_enabled_in_search']);
239
  $object->setPosition($values['position']);
240
  $object->setSortMethod(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/sort_method'));
 
241
  }
242
  /**
243
  * Enter description here ...
@@ -265,6 +275,8 @@ class Mana_Filters_Resource_Filter2 extends Mana_Db_Resource_Object {
265
  'target.code = eav_attribute.attribute_code'.$attributeJoin, null)
266
  ->joinLeft(array('eav_attribute_additional' => Mage::getSingleton('core/resource')->getTableName('catalog/eav_attribute')),
267
  'eav_attribute.attribute_id = eav_attribute_additional.attribute_id', null)
 
 
268
  ->distinct()
269
  ->where('(eav_attribute.attribute_id IS NULL) OR (eav_attribute_additional.is_filterable = 0)')
270
  ->where("target.code <> 'category'");
@@ -292,6 +304,7 @@ class Mana_Filters_Resource_Filter2 extends Mana_Db_Resource_Object {
292
  Mage::helper('mana_db')->updateDefaultableField($object, 'sort_method', Mana_Filters_Resource_Filter2::DM_SORT_METHOD, $fields, $useDefault);
293
  Mage::helper('mana_db')->updateDefaultableField($object, 'operation', Mana_Filters_Resource_Filter2::DM_OPERATION, $fields, $useDefault);
294
  Mage::helper('mana_db')->updateDefaultableField($object, 'is_reverse', Mana_Filters_Resource_Filter2::DM_IS_REVERSE, $fields, $useDefault);
 
295
  }
296
  protected function _afterSave(Mage_Core_Model_Abstract $object) {
297
  if ($edit = $object->getValueData()) {
53
  const DM_HELP_WIDTH = 33;
54
 
55
  const DM_IS_REVERSE = 34;
56
+
57
+ const DM_INCLUDE_IN_URL = 35;
58
+ const DM_URL_POSITION = 36;
59
+
60
+ const DM_DISABLE_NO_RESULT_OPTIONS = 37;
61
+
62
  #endregion
63
 
64
  /**
167
  if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, Mana_Filters_Resource_Filter2::DM_SORT_METHOD)) {
168
  $object->setSortMethod(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/sort_method'));
169
  }
170
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, Mana_Filters_Resource_Filter2::DM_DISABLE_NO_RESULT_OPTIONS)) {
171
+ $object->setDisableNoResultOptions(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/disable_no_result_options'));
172
+ }
173
  }
174
 
175
  /**
247
  $object->setIsEnabledInSearch($values['is_enabled_in_search']);
248
  $object->setPosition($values['position']);
249
  $object->setSortMethod(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/sort_method'));
250
+ $object->setDisableNoResultOptions(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/disable_no_result_options'));
251
  }
252
  /**
253
  * Enter description here ...
275
  'target.code = eav_attribute.attribute_code'.$attributeJoin, null)
276
  ->joinLeft(array('eav_attribute_additional' => Mage::getSingleton('core/resource')->getTableName('catalog/eav_attribute')),
277
  'eav_attribute.attribute_id = eav_attribute_additional.attribute_id', null)
278
+ ->joinLeft(array('eav_entity_type' => Mage::getSingleton('core/resource')->getTableName('eav/entity_type')),
279
+ "eav_entity_type.entity_type_id = eav_attribute.entity_type_id AND eav_entity_type.entity_type_code = 'catalog_product'", null)
280
  ->distinct()
281
  ->where('(eav_attribute.attribute_id IS NULL) OR (eav_attribute_additional.is_filterable = 0)')
282
  ->where("target.code <> 'category'");
304
  Mage::helper('mana_db')->updateDefaultableField($object, 'sort_method', Mana_Filters_Resource_Filter2::DM_SORT_METHOD, $fields, $useDefault);
305
  Mage::helper('mana_db')->updateDefaultableField($object, 'operation', Mana_Filters_Resource_Filter2::DM_OPERATION, $fields, $useDefault);
306
  Mage::helper('mana_db')->updateDefaultableField($object, 'is_reverse', Mana_Filters_Resource_Filter2::DM_IS_REVERSE, $fields, $useDefault);
307
+ Mage::helper('mana_db')->updateDefaultableField($object, 'disable_no_result_options', Mana_Filters_Resource_Filter2::DM_DISABLE_NO_RESULT_OPTIONS, $fields, $useDefault);
308
  }
309
  protected function _afterSave(Mage_Core_Model_Abstract $object) {
310
  if ($edit = $object->getValueData()) {
app/code/local/Mana/Filters/Resource/Filter2/Store.php CHANGED
@@ -53,6 +53,7 @@ class Mana_Filters_Resource_Filter2_Store extends Mana_Filters_Resource_Filter2
53
  'global.sort_method AS sort_method',
54
  'global.operation AS operation',
55
  'global.is_reverse AS is_reverse',
 
56
  ));
57
  if ($options['trackKeys']) {
58
  if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
@@ -107,6 +108,9 @@ class Mana_Filters_Resource_Filter2_Store extends Mana_Filters_Resource_Filter2
107
  if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, Mana_Filters_Resource_Filter2::DM_IS_REVERSE)) {
108
  $object->setIsReverse($values['is_reverse']);
109
  }
 
 
 
110
  }
111
  /**
112
  * Enter description here ...
@@ -137,6 +141,7 @@ class Mana_Filters_Resource_Filter2_Store extends Mana_Filters_Resource_Filter2
137
  'global.sort_method AS sort_method',
138
  'global.operation AS operation',
139
  'global.is_reverse AS is_reverse',
 
140
  ));
141
  if ($options['trackKeys']) {
142
  if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
@@ -170,6 +175,7 @@ class Mana_Filters_Resource_Filter2_Store extends Mana_Filters_Resource_Filter2
170
  $object->setSortMethod($values['sort_method']);
171
  $object->setOperation($values['operation']);
172
  $object->setIsReverse($values['is_reverse']);
 
173
  }
174
  /**
175
  * Enter description here ...
53
  'global.sort_method AS sort_method',
54
  'global.operation AS operation',
55
  'global.is_reverse AS is_reverse',
56
+ 'global.disable_no_result_options AS disable_no_result_options',
57
  ));
58
  if ($options['trackKeys']) {
59
  if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
108
  if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, Mana_Filters_Resource_Filter2::DM_IS_REVERSE)) {
109
  $object->setIsReverse($values['is_reverse']);
110
  }
111
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, Mana_Filters_Resource_Filter2::DM_DISABLE_NO_RESULT_OPTIONS)) {
112
+ $object->setDisableNoResultOptions($values['disable_no_result_options']);
113
+ }
114
  }
115
  /**
116
  * Enter description here ...
141
  'global.sort_method AS sort_method',
142
  'global.operation AS operation',
143
  'global.is_reverse AS is_reverse',
144
+ 'global.disable_no_result_options AS disable_no_result_options',
145
  ));
146
  if ($options['trackKeys']) {
147
  if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
175
  $object->setSortMethod($values['sort_method']);
176
  $object->setOperation($values['operation']);
177
  $object->setIsReverse($values['is_reverse']);
178
+ $object->setDisableNoResultOptions($values['disable_no_result_options']);
179
  }
180
  /**
181
  * Enter description here ...
app/code/local/Mana/Filters/Resource/Filter2/Value.php CHANGED
@@ -22,8 +22,32 @@ class Mana_Filters_Resource_Filter2_Value extends Mana_Db_Resource_Object {
22
  const DM_NORMAL_HOVERED_IMAGE = 5;
23
  const DM_SELECTED_HOVERED_IMAGE = 6;
24
  const DM_STATE_IMAGE = 7;
 
25
  #endregion
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  /**
28
  * Invoked during resource model creation process, this method associates this resource model with model class
29
  * and with DB table name
22
  const DM_NORMAL_HOVERED_IMAGE = 5;
23
  const DM_SELECTED_HOVERED_IMAGE = 6;
24
  const DM_STATE_IMAGE = 7;
25
+
26
  #endregion
27
 
28
+ public function loadByFilterPosition($object, $filterId, $position)
29
+ {
30
+ $read = $this->_getReadAdapter();
31
+
32
+ $select = $this->_getReadAdapter()->select()
33
+ ->from($this->getMainTable())
34
+ ->where($this->getMainTable() . '.' . 'filter_id' . '=?', $filterId)
35
+ ->where($this->getMainTable() . '.' . 'position' . '=?', $position);
36
+
37
+ if ($read) {
38
+ $data = $read->fetchRow($select);
39
+
40
+ if ($data) {
41
+ $object->setData($data);
42
+ }
43
+ }
44
+
45
+ $this->unserializeFields($object);
46
+ $this->_afterLoad($object);
47
+
48
+ return $this;
49
+ }
50
+
51
  /**
52
  * Invoked during resource model creation process, this method associates this resource model with model class
53
  * and with DB table name
app/code/local/Mana/Filters/Resource/Filter2/Value/Store.php CHANGED
@@ -122,7 +122,7 @@ class Mana_Filters_Resource_Filter2_Value_Store extends Mana_Filters_Resource_Fi
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')
@@ -168,4 +168,8 @@ class Mana_Filters_Resource_Filter2_Value_Store extends Mana_Filters_Resource_Fi
168
  $object->setName($values['name']);
169
  $object->setPosition($values['position']);
170
  }
 
 
 
 
171
  }
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')
168
  $object->setName($values['name']);
169
  $object->setPosition($values['position']);
170
  }
171
+
172
+ public function loadByFilterPosition($object, $filterId, $position) {
173
+ throw new Exception('Not implemented');
174
+ }
175
  }
app/code/local/Mana/Filters/Resource/Indexer/Source.php CHANGED
@@ -11,6 +11,9 @@
11
  */
12
  class Mana_Filters_Resource_Indexer_Source extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Eav_Source {
13
  public function reindexEntities($processIds) {
 
 
 
14
  $adapter = $this->_getWriteAdapter();
15
 
16
  $this->clearTemporaryIndexTable();
11
  */
12
  class Mana_Filters_Resource_Indexer_Source extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Eav_Source {
13
  public function reindexEntities($processIds) {
14
+ // if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
15
+ // return parent::reindexEntities($processIds);
16
+ // }
17
  $adapter = $this->_getWriteAdapter();
18
 
19
  $this->clearTemporaryIndexTable();
app/code/local/Mana/Filters/Resource/Solr/And/Attribute.php CHANGED
@@ -19,8 +19,6 @@ class Mana_Filters_Resource_Solr_And_Attribute extends Mana_Filters_Resource_Sol
19
  */
20
  public function applyToCollection($collection, $model, $value)
21
  {
22
- $engine = Mage::getResourceSingleton('enterprise_search/engine');
23
- $facetField = $engine->getSearchEngineFieldName($model->getAttributeModel(), 'nav');
24
- $collection->addFqFilter(array($facetField => array('and' => $value)));
25
  }
26
  }
19
  */
20
  public function applyToCollection($collection, $model, $value)
21
  {
22
+ $collection->addFqFilter(array($model->getFilterField() => array('and' => $value)));
 
 
23
  }
24
  }
app/code/local/Mana/Filters/Resource/Solr/Attribute.php CHANGED
@@ -13,14 +13,12 @@ class Mana_Filters_Resource_Solr_Attribute extends Mana_Filters_Resource_Filter_
13
  {
14
  /**
15
  * @param Enterprise_Search_Model_Resource_Collection $collection
16
- * @param Mana_Filters_Model_Filter_Attribute $model
17
  * @return Mana_Filters_Resource_Solr_Attribute
18
  */
19
  public function countOnCollection($collection, $model)
20
  {
21
- $engine = Mage::getResourceSingleton('enterprise_search/engine');
22
- $facetField = $engine->getSearchEngineFieldName($model->getAttributeModel(), 'nav');
23
- $collection->setFacetCondition($facetField);
24
 
25
  return $collection;
26
  }
@@ -33,8 +31,6 @@ class Mana_Filters_Resource_Solr_Attribute extends Mana_Filters_Resource_Filter_
33
  */
34
  public function applyToCollection($collection, $model, $value)
35
  {
36
- $engine = Mage::getResourceSingleton('enterprise_search/engine');
37
- $facetField = $engine->getSearchEngineFieldName($model->getAttributeModel(), 'nav');
38
- $collection->addFqFilter(array($facetField => array('or' => $value)));
39
  }
40
  }
13
  {
14
  /**
15
  * @param Enterprise_Search_Model_Resource_Collection $collection
16
+ * @param Mana_Filters_Model_Solr_Attribute $model
17
  * @return Mana_Filters_Resource_Solr_Attribute
18
  */
19
  public function countOnCollection($collection, $model)
20
  {
21
+ $collection->setFacetCondition($model->getFilterField());
 
 
22
 
23
  return $collection;
24
  }
31
  */
32
  public function applyToCollection($collection, $model, $value)
33
  {
34
+ $collection->addFqFilter(array($model->getFilterField() => array('or' => $value)));
 
 
35
  }
36
  }
app/code/local/Mana/Filters/Resource/Solr/Collection.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Resource_Solr_Collection extends Enterprise_Search_Model_Resource_Collection {
13
+ /**
14
+ * Flag that defines if faceted data needs to be loaded
15
+ *
16
+ * @var bool
17
+ */
18
+ protected $_facetedDataIsLoaded = false;
19
+
20
+ /**
21
+ * Load faceted data if not loaded
22
+ *
23
+ * @return Enterprise_Search_Model_Resource_Collection
24
+ */
25
+ public function loadFacetedData($additionalParams = array()) {
26
+ if (empty($this->_facetedConditions)) {
27
+ $this->_facetedData = array();
28
+
29
+ return $this;
30
+ }
31
+
32
+ list($query, $params) = $this->_prepareBaseParams();
33
+ $params['solr_params']['facet'] = 'on';
34
+ $params['facet'] = $this->_facetedConditions;
35
+ $params = array_merge($params, $additionalParams);
36
+
37
+ $result = $this->_engine->getResultForRequest($query, $params);
38
+ $this->_facetedData = isset($result['faceted_data']) ? $result['faceted_data'] : $result['facets'];
39
+ $this->_facetedDataIsLoaded = true;
40
+
41
+ return $this;
42
+ }
43
+
44
+ /**
45
+ * Return field faceted data from faceted search result
46
+ *
47
+ * @param string $field
48
+ *
49
+ * @return array
50
+ */
51
+ public function getFacetedData($field, $additionalParams = array()) {
52
+ if (!$this->_facetedDataIsLoaded) {
53
+ $this->loadFacetedData($additionalParams);
54
+ }
55
+
56
+ if (isset($this->_facetedData[$field])) {
57
+ return $this->_facetedData[$field];
58
+ }
59
+
60
+ return array();
61
+ }
62
+ }
app/code/local/Mana/Filters/Resource/Solr/Engine.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Resource_Solr_Engine extends Enterprise_Search_Model_Resource_Engine {
13
+ /**
14
+ * Retrieve results for search request
15
+ *
16
+ * @param string $query
17
+ * @param array $params
18
+ * @param string $entityType 'product'|'cms'
19
+ * @return array
20
+ */
21
+ public function getResultForRequest($query, $params = array(), $entityType = 'product') {
22
+ return $this->_adapter->search($query, $params);
23
+ }
24
+ }
app/code/local/Mana/Filters/Resource/Solr/Reverse/Attribute.php CHANGED
@@ -19,8 +19,6 @@ class Mana_Filters_Resource_Solr_Reverse_Attribute extends Mana_Filters_Resource
19
  */
20
  public function applyToCollection($collection, $model, $value)
21
  {
22
- $engine = Mage::getResourceSingleton('enterprise_search/engine');
23
- $facetField = $engine->getSearchEngineFieldName($model->getAttributeModel(), 'nav');
24
- $collection->addFqFilter(array($facetField => array('reverse' => $value)));
25
  }
26
  }
19
  */
20
  public function applyToCollection($collection, $model, $value)
21
  {
22
+ $collection->addFqFilter(array($model->getFilterField() => array('reverse' => $value)));
 
 
23
  }
24
  }
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>12.12.21.19</version>
16
  </Mana_Filters>
17
  </modules>
18
 
@@ -65,6 +65,12 @@
65
  <adapter_phpExtension>Mana_Filters_Model_Solr_Adapter_PhpExtension</adapter_phpExtension>
66
  </rewrite>
67
  </enterprise_search>
 
 
 
 
 
 
68
  <!-- BASED ON SNIPPET: Resources/Resource support (config.xml) -->
69
  <!-- This says that string 'mana_filters' corresponds to Mana_Filters_Resource pseudo-namespace in
70
  getResourceModel() calls. -->
@@ -135,6 +141,16 @@
135
  </mana_filters>
136
  </observers>
137
  </prepare_catalog_product_index_select>
 
 
 
 
 
 
 
 
 
 
138
  </events>
139
  <!-- INSERT HERE: blocks, models, ... -->
140
  </global>
@@ -221,7 +237,8 @@
221
  <reverse_resource>mana_filters/filter_reverse_attribute</reverse_resource>
222
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
223
  <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
224
- </list>
 
225
  <standard translate="title" module="mana_filters">
226
  <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
227
  <block>mana_filters/filter</block>
@@ -234,7 +251,8 @@
234
  <reverse_resource>mana_filters/filter_reverse_attribute</reverse_resource>
235
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
236
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
237
- </standard>
 
238
  </attribute>
239
  <price> <!-- available display options for price filter -->
240
  <!-- by default, filter items are displayed as HTML list -->
@@ -248,7 +266,8 @@
248
  <reverse_resource>mana_filters/filter_reverse_price</reverse_resource>
249
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
250
  <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
251
- </list>
 
252
  <standard translate="title" module="mana_filters">
253
  <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
254
  <block>mana_filters/filter</block>
@@ -259,7 +278,8 @@
259
  <reverse_resource>mana_filters/filter_reverse_price</reverse_resource>
260
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
261
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
262
- </standard>
 
263
  </price>
264
  <category> <!-- available display options for category filter -->
265
  <!-- by default, filter items are displayed as HTML list -->
@@ -270,7 +290,8 @@
270
  <solr_model>mana_filters/solr_category</solr_model>
271
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
272
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
273
- </standard>
 
274
  </category>
275
  <decimal> <!-- available display options for decimal filters -->
276
  <!-- by default, filter items are displayed as HTML list -->
@@ -284,7 +305,8 @@
284
  <reverse_resource>mana_filters/filter_reverse_decimal</reverse_resource>
285
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
286
  <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
287
- </list>
 
288
  <standard translate="title" module="mana_filters">
289
  <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
290
  <block>mana_filters/filter</block>
@@ -295,7 +317,8 @@
295
  <reverse_resource>mana_filters/filter_reverse_decimal</reverse_resource>
296
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
297
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
298
- </standard>
 
299
  </decimal>
300
  </display>
301
 
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>13.10.14.16</version>
16
  </Mana_Filters>
17
  </modules>
18
 
65
  <adapter_phpExtension>Mana_Filters_Model_Solr_Adapter_PhpExtension</adapter_phpExtension>
66
  </rewrite>
67
  </enterprise_search>
68
+ <enterprise_search_resource>
69
+ <rewrite>
70
+ <collection>Mana_Filters_Resource_Solr_Collection</collection>
71
+ <engine>Mana_Filters_Resource_Solr_Engine</engine>
72
+ </rewrite>
73
+ </enterprise_search_resource>
74
  <!-- BASED ON SNIPPET: Resources/Resource support (config.xml) -->
75
  <!-- This says that string 'mana_filters' corresponds to Mana_Filters_Resource pseudo-namespace in
76
  getResourceModel() calls. -->
141
  </mana_filters>
142
  </observers>
143
  </prepare_catalog_product_index_select>
144
+ <core_block_abstract_to_html_after><!-- this is event name this module listens for -->
145
+ <observers>
146
+ <mana_filters>
147
+ <class>mana_filters/observer</class>
148
+ <!-- model name of class containing event handler methods -->
149
+ <method>hideCmsContentOnFilteredPages</method>
150
+ <!-- event handler method name -->
151
+ </mana_filters>
152
+ </observers>
153
+ </core_block_abstract_to_html_after>
154
  </events>
155
  <!-- INSERT HERE: blocks, models, ... -->
156
  </global>
237
  <reverse_resource>mana_filters/filter_reverse_attribute</reverse_resource>
238
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
239
  <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
240
+ <popup_class>Mana/LayeredNavigation/Popup/List</popup_class>
241
+ </list>
242
  <standard translate="title" module="mana_filters">
243
  <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
244
  <block>mana_filters/filter</block>
251
  <reverse_resource>mana_filters/filter_reverse_attribute</reverse_resource>
252
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
253
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
254
+ <popup_class>Mana/LayeredNavigation/Popup/Standard</popup_class>
255
+ </standard>
256
  </attribute>
257
  <price> <!-- available display options for price filter -->
258
  <!-- by default, filter items are displayed as HTML list -->
266
  <reverse_resource>mana_filters/filter_reverse_price</reverse_resource>
267
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
268
  <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
269
+ <popup_class>Mana/LayeredNavigation/Popup/List</popup_class>
270
+ </list>
271
  <standard translate="title" module="mana_filters">
272
  <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
273
  <block>mana_filters/filter</block>
278
  <reverse_resource>mana_filters/filter_reverse_price</reverse_resource>
279
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
280
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
281
+ <popup_class>Mana/LayeredNavigation/Popup/Standard</popup_class>
282
+ </standard>
283
  </price>
284
  <category> <!-- available display options for category filter -->
285
  <!-- by default, filter items are displayed as HTML list -->
290
  <solr_model>mana_filters/solr_category</solr_model>
291
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
292
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
293
+ <popup_class>Mana/LayeredNavigation/Popup/Standard</popup_class>
294
+ </standard>
295
  </category>
296
  <decimal> <!-- available display options for decimal filters -->
297
  <!-- by default, filter items are displayed as HTML list -->
305
  <reverse_resource>mana_filters/filter_reverse_decimal</reverse_resource>
306
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
307
  <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
308
+ <popup_class>Mana/LayeredNavigation/Popup/List</popup_class>
309
+ </list>
310
  <standard translate="title" module="mana_filters">
311
  <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
312
  <block>mana_filters/filter</block>
317
  <reverse_resource>mana_filters/filter_reverse_decimal</reverse_resource>
318
  <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
319
  <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
320
+ <popup_class>Mana/LayeredNavigation/Popup/Standard</popup_class>
321
+ </standard>
322
  </decimal>
323
  </display>
324
 
app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-13.09.11.14-13.09.23.17.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ foreach (array('mana_filters/filter2', 'mana_filters/filter2_store') as $table) {
15
+ $installer->run("
16
+ ALTER TABLE `{$this->getTable($table)}` ADD COLUMN (
17
+ `disable_no_result_options` tinyint NOT NULL default '1'
18
+ );
19
+ ");
20
+ }
21
+
22
+ if (method_exists($this->getConnection(), 'disallowDdlCache')) {
23
+ $this->getConnection()->disallowDdlCache();
24
+ }
25
+ $installer->endSetup();
26
+
27
+ if (!Mage::registry('m_run_db_replication')) {
28
+ Mage::register('m_run_db_replication', true);
29
+ }
app/design/adminhtml/default/default/layout/mana_core.xml CHANGED
@@ -26,8 +26,11 @@ examples on what kind of actions can be applied to referenced blocks.
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>
26
  </default>
27
  <jquery_core>
28
  <reference name="head">
29
+ <!-- BEGIN: files which are managed in System->Configuration->MANAdev->General->JavaScript Libraries -->
30
+ <!--<action method="addJs"><script>jquery/require.js</script></action>-->
31
+ <action method="addJs"><script>jquery/jquery.js</script></action>
32
+ <!-- END: files which are managed in System->Configuration->MANAdev->General->JavaScript Libraries -->
33
+
34
  <action method="addJs"><script>jquery/jquery.printf.js</script></action>
35
  <action method="addJs"><script>mana/core.js</script></action>
36
  <action method="addCss"><stylesheet>css/mana_core.css</stylesheet></action>
app/design/adminhtml/default/default/template/mana/core/js.phtml CHANGED
@@ -6,6 +6,10 @@
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[
@@ -17,5 +21,10 @@
17
  $.options(<?php echo json_encode($_options) ?>);
18
  <?php endif; ?>
19
  })(jQuery);
 
 
 
 
 
20
  //]]>
21
  </script>
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
+ /* @var $core Mana_Core_Helper_Data */
11
+ $core = Mage::helper('mana_core');
12
+
13
  ?>
14
  <script type="text/javascript">
15
  //<![CDATA[
21
  $.options(<?php echo json_encode($_options) ?>);
22
  <?php endif; ?>
23
  })(jQuery);
24
+ <?php if ($_config = $this->getConfig()) : ?>
25
+ Mana.require(['singleton:Mana/Core/Config'], function(config) {
26
+ config.set(<?php echo $core->jsonForceObjectAndEncode($_config)?>);
27
+ });
28
+ <?php endif; ?>
29
  //]]>
30
  </script>
app/design/adminhtml/default/default/template/mana/core/require.phtml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://www.manadev.com/license Proprietary License
7
+ */
8
+ /* @var $this Mana_Core_Block_Require */
9
+ ?>
10
+ <script type="text/javascript">
11
+ var require = {
12
+ baseUrl: '<?php echo $this->getBaseRequireUrl() ?>'
13
+ };
14
+ </script>
app/design/frontend/base/default/layout/mana_core.xml CHANGED
@@ -26,8 +26,11 @@ examples on what kind of actions can be applied to referenced blocks.
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>
26
  </default>
27
  <jquery_core>
28
  <reference name="head">
29
+ <!-- BEGIN: files which are managed in System->Configuration->MANAdev->General->JavaScript Libraries -->
30
+ <!--<action method="addJs"><script>jquery/require.js</script></action>-->
31
+ <action method="addJs"><script>jquery/jquery.js</script></action>
32
+ <!-- END: files which are managed in System->Configuration->MANAdev->General->JavaScript Libraries -->
33
+
34
  <action method="addJs"><script>jquery/jquery.printf.js</script></action>
35
  <action method="addJs"><script>mana/core.js</script></action>
36
  <action method="addCss"><stylesheet>css/mana_core.css</stylesheet></action>
app/design/frontend/base/default/template/mana/core/js.phtml CHANGED
@@ -6,6 +6,10 @@
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[
@@ -16,6 +20,11 @@
16
  <?php if ($_options = $this->getOptions()) : ?>
17
  $.options(<?php echo json_encode($_options) ?>);
18
  <?php endif; ?>
 
 
 
 
 
19
  })(jQuery);
20
  //]]>
21
  </script>
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
+ /* @var $core Mana_Core_Helper_Data */
11
+ $core = Mage::helper('mana_core');
12
+
13
  ?>
14
  <script type="text/javascript">
15
  //<![CDATA[
20
  <?php if ($_options = $this->getOptions()) : ?>
21
  $.options(<?php echo json_encode($_options) ?>);
22
  <?php endif; ?>
23
+ <?php if ($_config = $this->getConfig()) : ?>
24
+ Mana.require(['singleton:Mana/Core/Config'], function (config) {
25
+ config.set(<?php echo $core->jsonForceObjectAndEncode($_config)?>);
26
+ });
27
+ <?php endif; ?>
28
  })(jQuery);
29
  //]]>
30
  </script>
app/design/frontend/base/default/template/mana/core/require.phtml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://www.manadev.com/license Proprietary License
7
+ */
8
+ /* @var $this Mana_Core_Block_Require */
9
+ ?>
10
+ <script type="text/javascript">
11
+ var require = {
12
+ baseUrl: '<?php echo $this->getBaseRequireUrl() ?>'
13
+ };
14
+ </script>
app/design/frontend/base/default/template/mana/filters/cms.phtml CHANGED
@@ -5,5 +5,25 @@
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 echo $this->getChildHtml('product_list') ?>
 
 
 
 
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
+ /* @var $this Mage_Core_Block_Template */
10
+
11
+ /* @var $_core Mana_Core_Helper_Data */
12
+ $_core = Mage::helper('mana_core');
13
+
14
+ /* @var $core Mana_Filters_Helper_Data */
15
+ $_filters = Mage::helper('mana_filters');
16
+
17
+ $_hideWhenNoFiltersApplied =
18
+ ($this->hasData('hide_when_no_filters_applied')
19
+ ? $this->getData('hide_when_no_filters_applied')
20
+ : Mage::getStoreConfigFlag('mana_filters/display/hide_cms_product_list'));
21
+ $_isFilterApplied = ($state = $_filters->getLayer()->getState()) && $state->getFilters();
22
+ $_isProductListVisible = $_isFilterApplied || !$_hideWhenNoFiltersApplied;
23
+
24
  ?>
25
+ <?php if ($_isProductListVisible) : ?>
26
+ <?php echo $this->getChildHtml('product_list') ?>
27
+ <?php else: ?>
28
+ <?php echo $_core->getEmptyBlockHtml($this->getChild('product_list')) ?>
29
+ <?php endif; ?>
app/design/frontend/base/default/template/mana/filters/items/list.phtml CHANGED
@@ -19,11 +19,11 @@
19
  <li <?php if ($_item->getMShowSelected()): ?>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; ?>
29
  <?php // MANA END ?>
19
  <li <?php if ($_item->getMShowSelected()): ?>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 $this->escapeHtml($_item->getLabel()) ?></span>
23
  <?php else : ?>
24
+ <?php if (!$this->getFilterOptions()->getDisableNoResultOptions() || $_item->getCount() > 0 || $_item->getMSelected()): ?>
25
+ <a href="<?php echo $this->urlEscape($_item->getUrl()) ?>" title="<?php echo $this->escapeHtml($_item->getLabel()) ?>"><?php echo $this->escapeHtml($_item->getLabel()) ?></a>
26
+ <?php else: echo $this->escapeHtml($_item->getLabel()) ?>
27
  <?php endif; ?>
28
  <?php endif; ?>
29
  <?php // MANA END ?>
app/design/frontend/base/default/template/mana/filters/items/list_popup.phtml CHANGED
@@ -11,6 +11,8 @@
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();
@@ -25,11 +27,13 @@ list($rowCount, $columnCount) = $showmore->getPopupDimensions($items, $MAX_ROW_C
25
  <?php $_item = $items[$columnIndex * $rowCount + $rowIndex]; ?>
26
  <li <?php if ($_item->getMShowSelected()): ?>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() ?>', <?php echo $this->getFilterOptions()->getIsReverse() ? '!' : '' ?> jQuery(this).parent().hasClass('m-selected-ln-item'));">
30
- <span <?php if ($_item->getMShowSelected()): ?>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 if (Mage::getStoreConfigFlag('mana_filters/display/count')) : ?>
35
  (<?php echo $_item->getCount() ?>)
@@ -41,11 +45,9 @@ list($rowCount, $columnCount) = $showmore->getPopupDimensions($items, $MAX_ROW_C
41
  <?php endfor; ?>
42
  </ol>
43
  <div class="buttons-set">
44
- <button type="button" title="<?php echo $this->__('Close') ?>" class="button"
45
- onclick="return jQuery.mClosePopup();">
46
  <span><span><?php echo $this->__('Close') ?></span></span></button>
47
- <button type="button" title="<?php echo $this->__('Apply') ?>" class="button"
48
- onclick="return jQuery.mShowMorePopupApply();">
49
  <span><span><?php echo $this->__('Apply') ?></span></span></button>
50
  </div>
51
  </div>
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
+ /* @var $jsonHelper Mana_Core_Helper_Json */
15
+ $jsonHelper = Mage::helper('mana_core/json');
16
  $MAX_ROW_COUNT = $showmore->getMaxRowCount(); // 20
17
  $MAX_COLUMN_COUNT = $showmore->getMaxColumnCount(); // 4
18
  $items = $this->getItems();
27
  <?php $_item = $items[$columnIndex * $rowCount + $rowIndex]; ?>
28
  <li <?php if ($_item->getMShowSelected()): ?>class="m-selected-ln-item"<?php endif; ?>>
29
  <?php //if ($_item->getCount() > 0): ?>
30
+ <a href="#" title="<?php echo $this->escapeHtml($_item->getLabel()) ?>"
31
+ data-item="<?php echo $jsonHelper->encodeAttribute($_item->getSeoData()) ?>"
32
+ data-is-reverse="<?php echo $this->getFilterOptions()->getIsReverse() ?>"
33
+ data-index="<?php echo $columnIndex * $rowCount + $rowIndex ?>">
34
+ <span <?php if ($_item->getMShowSelected()): ?>class="m-selected-filter-item"<?php endif; ?> ><?php echo $this->escapeHtml($_item->getLabel()) ?></span>
35
  </a>
36
+ <?php //else: echo $this->escapeHtml($_item->getLabel()); ?>
37
  <?php //endif; ?>
38
  <?php if (Mage::getStoreConfigFlag('mana_filters/display/count')) : ?>
39
  (<?php echo $_item->getCount() ?>)
45
  <?php endfor; ?>
46
  </ol>
47
  <div class="buttons-set">
48
+ <button type="button" title="<?php echo $this->__('Close') ?>" class="button m-close">
 
49
  <span><span><?php echo $this->__('Close') ?></span></span></button>
50
+ <button type="button" title="<?php echo $this->__('Apply') ?>" class="button m-apply">
 
51
  <span><span><?php echo $this->__('Apply') ?></span></span></button>
52
  </div>
53
  </div>
app/design/frontend/base/default/template/mana/filters/items/standard.phtml CHANGED
@@ -19,11 +19,11 @@
19
  <li <?php if ($_item->getMShowSelected()): ?>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 ?>
19
  <li <?php if ($_item->getMShowSelected()): ?>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 $this->escapeHtml($_item->getLabel()) ?></span>
23
  <?php else : ?>
24
+ <?php if (!$this->getFilterOptions()->getDisableNoResultOptions() || $_item->getCount() > 0 || $_item->getMSelected()): ?>
25
+ <a href="<?php echo $this->urlEscape($_item->getReplaceUrl()) ?>" title="<?php echo $this->escapeHtml($_item->getLabel()) ?>"><?php echo $this->escapeHtml($_item->getLabel()) ?></a>
26
+ <?php else: echo $this->escapeHtml($_item->getLabel()) ?>
27
  <?php endif; ?>
28
  <?php endif; ?>
29
  <?php // MANA END ?>
app/design/frontend/base/default/template/mana/filters/items/standard_popup.phtml CHANGED
@@ -11,6 +11,8 @@
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();
@@ -25,12 +27,15 @@ list($rowCount, $columnCount) = $showmore->getPopupDimensions($items, $MAX_ROW_C
25
  <?php $_item = $items[$columnIndex * $rowCount + $rowIndex]; ?>
26
  <li <?php if ($_item->getMShowSelected()): ?>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 if (Mage::getStoreConfigFlag('mana_filters/display/count')) : ?>
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
+ /* @var $jsonHelper Mana_Core_Helper_Json */
15
+ $jsonHelper = Mage::helper('mana_core/json');
16
  $MAX_ROW_COUNT = $showmore->getMaxRowCount(); // 20
17
  $MAX_COLUMN_COUNT = $showmore->getMaxColumnCount(); // 4
18
  $items = $this->getItems();
27
  <?php $_item = $items[$columnIndex * $rowCount + $rowIndex]; ?>
28
  <li <?php if ($_item->getMShowSelected()): ?>class="m-selected-ln-item"<?php endif; ?>>
29
  <?php if ($_item->getMSelected()): ?>
30
+ <span class="m-selected-filter-item"><?php echo $this->escapeHtml($_item->getLabel()) ?></span>
31
  <?php else : ?>
32
+ <?php if (!$this->getFilterOptions()->getDisableNoResultOptions() || $_item->getCount() > 0 || $_item->getMSelected()): ?>
33
+ <a href="#" title="<?php echo $this->escapeHtml($_item->getLabel()) ?>"
34
+ data-item="<?php echo $jsonHelper->encodeAttribute($_item->getSeoData()) ?>"
35
+ data-is-reverse="<?php echo $this->getFilterOptions()->getIsReverse() ?>"
36
+ data-index="<?php echo $columnIndex * $rowCount + $rowIndex ?>">
37
+ <?php echo $this->escapeHtml($_item->getLabel()) ?></a>
38
+ <?php else: echo $this->escapeHtml($_item->getLabel()) ?>
39
  <?php endif; ?>
40
  <?php endif; ?>
41
  <?php if (Mage::getStoreConfigFlag('mana_filters/display/count')) : ?>
app/design/frontend/base/default/template/mana/filters/state.phtml CHANGED
@@ -12,6 +12,7 @@
12
  *
13
  * @see Mage_Catalog_Block_Layer_State
14
  */
 
15
  ?>
16
  <?php $_filters = $this->getActiveFilters() ?>
17
  <?php if(!empty($_filters)): ?>
12
  *
13
  * @see Mage_Catalog_Block_Layer_State
14
  */
15
+ /* @var $this Mage_Catalog_Block_Layer_State */
16
  ?>
17
  <?php $_filters = $this->getActiveFilters() ?>
18
  <?php if(!empty($_filters)): ?>
app/etc/modules/Mana_Core.xml CHANGED
@@ -15,6 +15,12 @@
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>
15
  <active>true</active>
16
  <!-- This instructs Magento to search for module code in app/code/local directory. -->
17
  <codePool>local</codePool>
18
+
19
+ <Enterprise_PageCache>
20
+ <depends>
21
+ <Mana_Core />
22
+ </depends>
23
+ </Enterprise_PageCache>
24
  <!-- INSERT HERE: depends -->
25
  </Mana_Core>
26
  </modules>
app/locale/en_US/Mana_Core.csv CHANGED
@@ -17,3 +17,19 @@
17
  "Configure Mana Extensions","Configure Mana Extensions"
18
  "Please wait...","Please wait..."
19
  "Loading...","Loading..."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  "Configure Mana Extensions","Configure Mana Extensions"
18
  "Please wait...","Please wait..."
19
  "Loading...","Loading..."
20
+ "General","General"
21
+ "MANAdev General Settings","MANAdev General Settings"
22
+ "JavaScript Libraries","JavaScript Libraries"
23
+
24
+ "jQuery (version 1.8.3)","jQuery (version 1.8.3)"
25
+
26
+ "Always runs in jQuery.noConflict() mode (slight modification) to prevent conflicts with Prototype JavaScript library used by Magento.","Always runs in jQuery.noConflict() mode (slight modification) to prevent conflicts with Prototype JavaScript library used by Magento."
27
+ "Load if required, minified version (recommended)","Load if required, minified version (recommended)"
28
+ "Load on all pages, minified version","Load on all pages, minified version"
29
+ "Load if required, full version with comments","Load if required, full version with comments"
30
+ "Load on all pages, full version with comments","Load on all pages, full version with comments"
31
+ "Do not load","Do not load"
32
+ "Take action as defined in theme layout XML files","Take action as defined in theme layout XML files"
33
+ "Use Default","Use Default"
34
+ "Advices, Warnings and Messages","Advices, Warnings and Messages"
35
+ "Global Configuration","Global Configuration"
app/locale/en_US/Mana_Db.csv CHANGED
@@ -9,6 +9,18 @@
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"
 
 
 
 
 
 
 
 
 
 
 
 
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 (MANAdev)","Default Values (MANAdev)"
13
  "Propagate default values throughout the system","Propagate default values throughout the system"
14
  "Use Default","Use Default"
15
+ "Setup version %s not found","Setup version %s not found"
16
+ "Entity Flat Data (MANAdev)","Entity Flat Data (MANAdev)"
17
+ "Reorganizes entity data to flat structure","Reorganizes entity data to flat structure"
18
+ "",""
19
+ "",""
20
+ "",""
21
+ "",""
22
+ "",""
23
+ "",""
24
+ "",""
25
+ "",""
26
+ "",""
app/locale/en_US/Mana_Filters.csv CHANGED
@@ -10,7 +10,7 @@
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"
@@ -21,4 +21,6 @@
21
  "Logical AND","Logical AND"
22
  "Reverse Mode","Reverse Mode"
23
  "If enabled, shows all items as selected and lets user to deselect items he/she is not interested in","If enabled, shows all items as selected and lets user to deselect items he/she is not interested in"
24
- "not","not"
 
 
10
  "Can Store Customer Select Multiple Values","Can Store Customer Select Multiple Values"
11
  "Code","Code"
12
  "Name","Name"
13
+ "Use Attribute Configuration","Use Attribute Configuration"
14
  "Display Filter Items As","Display Filter Items As"
15
  "Position (selected at the top)","Position (selected at the top)"
16
  "Name","Name"
21
  "Logical AND","Logical AND"
22
  "Reverse Mode","Reverse Mode"
23
  "If enabled, shows all items as selected and lets user to deselect items he/she is not interested in","If enabled, shows all items as selected and lets user to deselect items he/she is not interested in"
24
+ "not","not"
25
+ "Layered Navigation (MANAdev)","Layered Navigation (MANAdev)"
26
+ "Recalculates formula-based layered navigation settings","Recalculates formula-based layered navigation settings"
js/jquery/froogaloop.js ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ; // for better merging
2
+
3
+ // Init style shamelessly stolen from jQuery http://jquery.com
4
+ var Froogaloop = (function(){
5
+ // Define a local copy of Froogaloop
6
+ function Froogaloop(iframe) {
7
+ // The Froogaloop object is actually just the init constructor
8
+ return new Froogaloop.fn.init(iframe);
9
+ }
10
+
11
+ var eventCallbacks = {},
12
+ hasWindowEvent = false,
13
+ isReady = false,
14
+ slice = Array.prototype.slice,
15
+ playerDomain = '';
16
+
17
+ Froogaloop.fn = Froogaloop.prototype = {
18
+ element: null,
19
+
20
+ init: function(iframe) {
21
+ if (typeof iframe === "string") {
22
+ iframe = document.getElementById(iframe);
23
+ }
24
+
25
+ this.element = iframe;
26
+
27
+ // Register message event listeners
28
+ playerDomain = getDomainFromUrl(this.element.getAttribute('src'));
29
+
30
+ return this;
31
+ },
32
+
33
+ /*
34
+ * Calls a function to act upon the player.
35
+ *
36
+ * @param {string} method The name of the Javascript API method to call. Eg: 'play'.
37
+ * @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
38
+ * or callback function when the method returns a value.
39
+ */
40
+ api: function(method, valueOrCallback) {
41
+ if (!this.element || !method) {
42
+ return false;
43
+ }
44
+
45
+ var self = this,
46
+ element = self.element,
47
+ target_id = element.id !== '' ? element.id : null,
48
+ params = !isFunction(valueOrCallback) ? valueOrCallback : null,
49
+ callback = isFunction(valueOrCallback) ? valueOrCallback : null;
50
+
51
+ // Store the callback for get functions
52
+ if (callback) {
53
+ storeCallback(method, callback, target_id);
54
+ }
55
+
56
+ postMessage(method, params, element);
57
+ return self;
58
+ },
59
+
60
+ /*
61
+ * Registers an event listener and a callback function that gets called when the event fires.
62
+ *
63
+ * @param eventName (String): Name of the event to listen for.
64
+ * @param callback (Function): Function that should be called when the event fires.
65
+ */
66
+ addEvent: function(eventName, callback) {
67
+ if (!this.element) {
68
+ return false;
69
+ }
70
+
71
+ var self = this,
72
+ element = self.element,
73
+ target_id = element.id !== '' ? element.id : null;
74
+
75
+
76
+ storeCallback(eventName, callback, target_id);
77
+
78
+ // The ready event is not registered via postMessage. It fires regardless.
79
+ if (eventName != 'ready') {
80
+ postMessage('addEventListener', eventName, element);
81
+ }
82
+ else if (eventName == 'ready' && isReady) {
83
+ callback.call(null, target_id);
84
+ }
85
+
86
+ return self;
87
+ },
88
+
89
+ /*
90
+ * Unregisters an event listener that gets called when the event fires.
91
+ *
92
+ * @param eventName (String): Name of the event to stop listening for.
93
+ */
94
+ removeEvent: function(eventName) {
95
+ if (!this.element) {
96
+ return false;
97
+ }
98
+
99
+ var self = this,
100
+ element = self.element,
101
+ target_id = element.id !== '' ? element.id : null,
102
+ removed = removeCallback(eventName, target_id);
103
+
104
+ // The ready event is not registered
105
+ if (eventName != 'ready' && removed) {
106
+ postMessage('removeEventListener', eventName, element);
107
+ }
108
+ }
109
+ };
110
+
111
+ /**
112
+ * Handles posting a message to the parent window.
113
+ *
114
+ * @param method (String): name of the method to call inside the player. For api calls
115
+ * this is the name of the api method (api_play or api_pause) while for events this method
116
+ * is api_addEventListener.
117
+ * @param params (Object or Array): List of parameters to submit to the method. Can be either
118
+ * a single param or an array list of parameters.
119
+ * @param target (HTMLElement): Target iframe to post the message to.
120
+ */
121
+ function postMessage(method, params, target) {
122
+ if (!target.contentWindow.postMessage) {
123
+ return false;
124
+ }
125
+
126
+ var url = target.getAttribute('src').split('?')[0],
127
+ data = JSON.stringify({
128
+ method: method,
129
+ value: params
130
+ });
131
+
132
+ if (url.substr(0, 2) === '//') {
133
+ url = window.location.protocol + url;
134
+ }
135
+
136
+ target.contentWindow.postMessage(data, url);
137
+ }
138
+
139
+ /**
140
+ * Event that fires whenever the window receives a message from its parent
141
+ * via window.postMessage.
142
+ */
143
+ function onMessageReceived(event) {
144
+ var data, method;
145
+
146
+ try {
147
+ data = JSON.parse(event.data);
148
+ method = data.event || data.method;
149
+ }
150
+ catch(e) {
151
+ //fail silently... like a ninja!
152
+ }
153
+
154
+ if (method == 'ready' && !isReady) {
155
+ isReady = true;
156
+ }
157
+
158
+ // Handles messages from moogaloop only
159
+ if (event.origin != playerDomain) {
160
+ return false;
161
+ }
162
+
163
+ var value = data.value,
164
+ eventData = data.data,
165
+ target_id = target_id === '' ? null : data.player_id,
166
+
167
+ callback = getCallback(method, target_id),
168
+ params = [];
169
+
170
+ if (!callback) {
171
+ return false;
172
+ }
173
+
174
+ if (value !== undefined) {
175
+ params.push(value);
176
+ }
177
+
178
+ if (eventData) {
179
+ params.push(eventData);
180
+ }
181
+
182
+ if (target_id) {
183
+ params.push(target_id);
184
+ }
185
+
186
+ return params.length > 0 ? callback.apply(null, params) : callback.call();
187
+ }
188
+
189
+
190
+ /**
191
+ * Stores submitted callbacks for each iframe being tracked and each
192
+ * event for that iframe.
193
+ *
194
+ * @param eventName (String): Name of the event. Eg. api_onPlay
195
+ * @param callback (Function): Function that should get executed when the
196
+ * event is fired.
197
+ * @param target_id (String) [Optional]: If handling more than one iframe then
198
+ * it stores the different callbacks for different iframes based on the iframe's
199
+ * id.
200
+ */
201
+ function storeCallback(eventName, callback, target_id) {
202
+ if (target_id) {
203
+ if (!eventCallbacks[target_id]) {
204
+ eventCallbacks[target_id] = {};
205
+ }
206
+ eventCallbacks[target_id][eventName] = callback;
207
+ }
208
+ else {
209
+ eventCallbacks[eventName] = callback;
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Retrieves stored callbacks.
215
+ */
216
+ function getCallback(eventName, target_id) {
217
+ if (target_id) {
218
+ return eventCallbacks[target_id][eventName];
219
+ }
220
+ else {
221
+ return eventCallbacks[eventName];
222
+ }
223
+ }
224
+
225
+ function removeCallback(eventName, target_id) {
226
+ if (target_id && eventCallbacks[target_id]) {
227
+ if (!eventCallbacks[target_id][eventName]) {
228
+ return false;
229
+ }
230
+ eventCallbacks[target_id][eventName] = null;
231
+ }
232
+ else {
233
+ if (!eventCallbacks[eventName]) {
234
+ return false;
235
+ }
236
+ eventCallbacks[eventName] = null;
237
+ }
238
+
239
+ return true;
240
+ }
241
+
242
+ /**
243
+ * Returns a domain's root domain.
244
+ * Eg. returns http://vimeo.com when http://vimeo.com/channels is sbumitted
245
+ *
246
+ * @param url (String): Url to test against.
247
+ * @return url (String): Root domain of submitted url
248
+ */
249
+ function getDomainFromUrl(url) {
250
+ if (url.substr(0, 2) === '//') {
251
+ url = window.location.protocol + url;
252
+ }
253
+
254
+ var url_pieces = url.split('/'),
255
+ domain_str = '';
256
+
257
+ for(var i = 0, length = url_pieces.length; i < length; i++) {
258
+ if(i<3) {domain_str += url_pieces[i];}
259
+ else {break;}
260
+ if(i<2) {domain_str += '/';}
261
+ }
262
+
263
+ return domain_str;
264
+ }
265
+
266
+ function isFunction(obj) {
267
+ return !!(obj && obj.constructor && obj.call && obj.apply);
268
+ }
269
+
270
+ function isArray(obj) {
271
+ return toString.call(obj) === '[object Array]';
272
+ }
273
+
274
+ // Give the init function the Froogaloop prototype for later instantiation
275
+ Froogaloop.fn.init.prototype = Froogaloop.fn;
276
+
277
+ // Listens for the message event.
278
+ // W3C
279
+ if (window.addEventListener) {
280
+ window.addEventListener('message', onMessageReceived, false);
281
+ }
282
+ // IE
283
+ else {
284
+ window.attachEvent('onmessage', onMessageReceived);
285
+ }
286
+
287
+ // Expose froogaloop to the global object
288
+ return (window.Froogaloop = window.$f = Froogaloop);
289
+
290
+ })();
js/jquery/froogaloop.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ ; // for better merging
2
+ var Froogaloop=function(){function e(a){return new e.fn.init(a)}function h(a,c,b){if(!b.contentWindow.postMessage)return!1;var f=b.getAttribute("src").split("?")[0],a=JSON.stringify({method:a,value:c});"//"===f.substr(0,2)&&(f=window.location.protocol+f);b.contentWindow.postMessage(a,f)}function j(a){var c,b;try{c=JSON.parse(a.data),b=c.event||c.method}catch(f){}"ready"==b&&!i&&(i=!0);if(a.origin!=k)return!1;var a=c.value,e=c.data,g=""===g?null:c.player_id;c=g?d[g][b]:d[b];b=[];if(!c)return!1;void 0!==
3
+ a&&b.push(a);e&&b.push(e);g&&b.push(g);return 0<b.length?c.apply(null,b):c.call()}function l(a,c,b){b?(d[b]||(d[b]={}),d[b][a]=c):d[a]=c}var d={},i=!1,k="";e.fn=e.prototype={element:null,init:function(a){"string"===typeof a&&(a=document.getElementById(a));this.element=a;a=this.element.getAttribute("src");"//"===a.substr(0,2)&&(a=window.location.protocol+a);for(var a=a.split("/"),c="",b=0,f=a.length;b<f;b++){if(3>b)c+=a[b];else break;2>b&&(c+="/")}k=c;return this},api:function(a,c){if(!this.element||
4
+ !a)return!1;var b=this.element,f=""!==b.id?b.id:null,d=!c||!c.constructor||!c.call||!c.apply?c:null,e=c&&c.constructor&&c.call&&c.apply?c:null;e&&l(a,e,f);h(a,d,b);return this},addEvent:function(a,c){if(!this.element)return!1;var b=this.element,d=""!==b.id?b.id:null;l(a,c,d);"ready"!=a?h("addEventListener",a,b):"ready"==a&&i&&c.call(null,d);return this},removeEvent:function(a){if(!this.element)return!1;var c=this.element,b;a:{if((b=""!==c.id?c.id:null)&&d[b]){if(!d[b][a]){b=!1;break a}d[b][a]=null}else{if(!d[a]){b=
5
+ !1;break a}d[a]=null}b=!0}"ready"!=a&&b&&h("removeEventListener",a,c)}};e.fn.init.prototype=e.fn;window.addEventListener?window.addEventListener("message",j,!1):window.attachEvent("onmessage",j);return window.Froogaloop=window.$f=e}();
js/jquery/history.js CHANGED
@@ -1026,15 +1026,20 @@
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
  };
1026
  var result = str;
1027
 
1028
  // Unescape hash
1029
+ var tmp, success = false;
1030
+ for (var i = 0; i < 3; i++) {
1031
+ tmp = window.unescape(encodeURIComponent(result));
1032
  if ( tmp === result ) {
1033
+ success = true;
1034
  break;
1035
  }
1036
  result = tmp;
1037
  }
1038
 
1039
+ if (!success) {
1040
+ throw 'History.unescapeString(): unable to unescape ' + str;
1041
+ }
1042
+
1043
  // Return result
1044
  return result;
1045
  };
js/jquery/jquery-1.7.2.min.js DELETED
@@ -1,6 +0,0 @@
1
- /*! jQuery v1.7.2 jquery.com | jquery.org/license */
2
- (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
3
- a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
4
- .clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
5
-
6
- jQuery.noConflict();
 
 
 
 
 
 
js/jquery/jquery-ui.js CHANGED
@@ -1,43 +1,28 @@
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,
@@ -48,46 +33,43 @@ $.extend( $.ui, {
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 ) {
@@ -119,95 +101,63 @@ $.fn.extend({
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" ) ) );
@@ -220,77 +170,137 @@ $.extend( $.expr[ ":" ], {
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
@@ -298,65 +308,33 @@ $.extend( $.ui, {
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
 
@@ -366,81 +344,167 @@ $.widget = function( name, base, prototype ) {
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
  }
@@ -449,74 +513,123 @@ $.widget.bridge = function( name, object ) {
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 );
@@ -524,10 +637,11 @@ $.Widget.prototype = {
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
  },
@@ -536,10 +650,10 @@ $.Widget.prototype = {
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;
@@ -552,6 +666,97 @@ $.Widget.prototype = {
552
  return this._setOption( "disabled", true );
553
  },
554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
  _trigger: function( type, event, data ) {
556
  var prop, orig,
557
  callback = this.options[ type ];
@@ -576,49 +781,74 @@ $.Widget.prototype = {
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
  }
@@ -630,23 +860,28 @@ $.widget("ui.mouse", {
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
  }
@@ -654,7 +889,7 @@ $.widget("ui.mouse", {
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
 
@@ -667,30 +902,30 @@ $.widget("ui.mouse", {
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
 
@@ -710,14 +945,14 @@ $.widget("ui.mouse", {
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);
@@ -734,383 +969,74 @@ $.widget("ui.mouse", {
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) {
@@ -1118,26 +1044,26 @@ $.widget("ui.draggable", $.ui.mouse, {
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
  },
@@ -1149,12 +1075,15 @@ $.widget("ui.draggable", $.ui.mouse, {
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 -
@@ -1189,12 +1118,13 @@ $.widget("ui.draggable", $.ui.mouse, {
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) {
@@ -1206,15 +1136,18 @@ $.widget("ui.draggable", $.ui.mouse, {
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
 
@@ -1227,16 +1160,22 @@ $.widget("ui.draggable", $.ui.mouse, {
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
  },
@@ -1244,25 +1183,35 @@ $.widget("ui.draggable", $.ui.mouse, {
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 {
@@ -1273,78 +1222,73 @@ $.widget("ui.draggable", $.ui.mouse, {
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
  },
@@ -1359,14 +1303,17 @@ $.widget("ui.draggable", $.ui.mouse, {
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),
@@ -1377,7 +1324,7 @@ $.widget("ui.draggable", $.ui.mouse, {
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(),
@@ -1407,30 +1354,40 @@ $.widget("ui.draggable", $.ui.mouse, {
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
 
@@ -1438,22 +1395,25 @@ $.widget("ui.draggable", $.ui.mouse, {
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
 
@@ -1461,9 +1421,12 @@ $.widget("ui.draggable", $.ui.mouse, {
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 -
@@ -1471,50 +1434,57 @@ $.widget("ui.draggable", $.ui.mouse, {
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
 
@@ -1522,8 +1492,9 @@ $.widget("ui.draggable", $.ui.mouse, {
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
  },
@@ -1533,13 +1504,16 @@ $.widget("ui.draggable", $.ui.mouse, {
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,
@@ -1550,18 +1524,14 @@ $.widget("ui.draggable", $.ui.mouse, {
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,
@@ -1576,7 +1546,7 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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() {
@@ -1587,8 +1557,10 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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);
@@ -1596,8 +1568,9 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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
@@ -1609,26 +1582,36 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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
 
@@ -1636,7 +1619,7 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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
 
@@ -1659,7 +1642,9 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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
 
@@ -1669,25 +1654,27 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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
 
@@ -1695,212 +1682,257 @@ $.ui.plugin.add("draggable", "connectToSortable", {
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);
@@ -1917,23 +1949,22 @@ $.widget("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
  };
@@ -1943,24 +1974,38 @@ $.widget("ui.droppable", {
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
  },
@@ -1968,37 +2013,53 @@ $.widget("ui.droppable", {
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
 
@@ -2017,50 +2078,42 @@ $.widget("ui.droppable", {
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
  };
@@ -2070,20 +2123,38 @@ $.ui.intersect = function(draggable, droppable, toleranceMode) {
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 };
@@ -2094,14 +2165,19 @@ $.ui.ddmanager = {
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
 
@@ -2111,77 +2187,89 @@ $.ui.ddmanager = {
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,
@@ -2199,11 +2287,19 @@ $.widget("ui.resizable", $.ui.mouse, {
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, {
@@ -2211,30 +2307,26 @@ $.widget("ui.resizable", $.ui.mouse, {
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;
@@ -2244,42 +2336,46 @@ $.widget("ui.resizable", $.ui.mouse, {
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
 
@@ -2287,26 +2383,29 @@ $.widget("ui.resizable", $.ui.mouse, {
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
 
@@ -2315,25 +2414,26 @@ $.widget("ui.resizable", $.ui.mouse, {
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
 
@@ -2342,16 +2442,20 @@ $.widget("ui.resizable", $.ui.mouse, {
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
  }
@@ -2361,66 +2465,70 @@ $.widget("ui.resizable", $.ui.mouse, {
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;
@@ -2437,10 +2545,10 @@ $.widget("ui.resizable", $.ui.mouse, {
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);
@@ -2450,38 +2558,60 @@ $.widget("ui.resizable", $.ui.mouse, {
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
  },
@@ -2489,84 +2619,114 @@ $.widget("ui.resizable", $.ui.mouse, {
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
  }
@@ -2574,62 +2734,80 @@ $.widget("ui.resizable", $.ui.mouse, {
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
 
@@ -2640,18 +2818,14 @@ $.widget("ui.resizable", $.ui.mouse, {
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
 
@@ -2666,15 +2840,15 @@ $.widget("ui.resizable", $.ui.mouse, {
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) {
@@ -2696,7 +2870,7 @@ $.widget("ui.resizable", $.ui.mouse, {
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: {},
@@ -2715,129 +2889,43 @@ $.widget("ui.resizable", $.ui.mouse, {
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
  }
@@ -2848,18 +2936,25 @@ $.ui.plugin.add("resizable", "animate", {
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
  };
@@ -2867,185 +2962,290 @@ $.ui.plugin.add("resizable", "containment", {
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,
@@ -3054,9 +3254,9 @@ $.widget("ui.selectable", $.ui.mouse, {
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
  };
@@ -3069,28 +3269,24 @@ $.widget("ui.selectable", $.ui.mouse, {
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
 
@@ -3099,8 +3295,8 @@ $.widget("ui.selectable", $.ui.mouse, {
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
  });
@@ -3109,25 +3305,26 @@ $.widget("ui.selectable", $.ui.mouse, {
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");
@@ -3136,11 +3333,11 @@ $.widget("ui.selectable", $.ui.mouse, {
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
  }
@@ -3151,46 +3348,55 @@ $.widget("ui.selectable", $.ui.mouse, {
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
  }
@@ -3198,32 +3404,32 @@ $.widget("ui.selectable", $.ui.mouse, {
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
  }
@@ -3235,28 +3441,26 @@ $.widget("ui.selectable", $.ui.mouse, {
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
  });
@@ -3269,35 +3473,30 @@ $.widget("ui.selectable", $.ui.mouse, {
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,
@@ -3305,7 +3504,7 @@ $.widget("ui.sortable", $.ui.mouse, {
3305
  grid: false,
3306
  handle: false,
3307
  helper: "original",
3308
- items: '> *',
3309
  opacity: false,
3310
  placeholder: false,
3311
  revert: false,
@@ -3314,7 +3513,21 @@ $.widget("ui.sortable", $.ui.mouse, {
3314
  scrollSpeed: 20,
3315
  scope: "default",
3316
  tolerance: "intersect",
3317
- zIndex: 1000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3318
  },
3319
  _create: function() {
3320
 
@@ -3326,7 +3539,7 @@ $.widget("ui.sortable", $.ui.mouse, {
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();
@@ -3334,15 +3547,19 @@ $.widget("ui.sortable", $.ui.mouse, {
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
  },
@@ -3350,9 +3567,8 @@ $.widget("ui.sortable", $.ui.mouse, {
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);
@@ -3360,32 +3576,44 @@ $.widget("ui.sortable", $.ui.mouse, {
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;
@@ -3396,7 +3624,9 @@ $.widget("ui.sortable", $.ui.mouse, {
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
@@ -3426,11 +3656,6 @@ $.widget("ui.sortable", $.ui.mouse, {
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,
@@ -3440,19 +3665,24 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -3460,47 +3690,63 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -3511,6 +3757,9 @@ $.widget("ui.sortable", $.ui.mouse, {
3511
  },
3512
 
3513
  _mouseDrag: function(event) {
 
 
 
3514
 
3515
  //Compute the helpers position
3516
  this.position = this._generatePosition(event);
@@ -3522,61 +3771,86 @@ $.widget("ui.sortable", $.ui.mouse, {
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;
@@ -3591,10 +3865,12 @@ $.widget("ui.sortable", $.ui.mouse, {
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;
@@ -3603,23 +3879,30 @@ $.widget("ui.sortable", $.ui.mouse, {
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);
@@ -3631,22 +3914,21 @@ $.widget("ui.sortable", $.ui.mouse, {
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
  }
@@ -3655,8 +3937,12 @@ $.widget("ui.sortable", $.ui.mouse, {
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,
@@ -3678,28 +3964,33 @@ $.widget("ui.sortable", $.ui.mouse, {
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
  },
@@ -3710,73 +4001,71 @@ $.widget("ui.sortable", $.ui.mouse, {
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) {
@@ -3787,37 +4076,35 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -3827,14 +4114,14 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -3842,32 +4129,33 @@ $.widget("ui.sortable", $.ui.mouse, {
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,
@@ -3875,8 +4163,8 @@ $.widget("ui.sortable", $.ui.mouse, {
3875
  width: 0, height: 0,
3876
  left: 0, top: 0
3877
  });
3878
- };
3879
- };
3880
 
3881
  },
3882
 
@@ -3887,105 +4175,124 @@ $.widget("ui.sortable", $.ui.mouse, {
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;
@@ -3993,79 +4300,116 @@ $.widget("ui.sortable", $.ui.mouse, {
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
  },
@@ -4081,14 +4425,16 @@ $.widget("ui.sortable", $.ui.mouse, {
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),
@@ -4099,7 +4445,7 @@ $.widget("ui.sortable", $.ui.mouse, {
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(),
@@ -4127,19 +4473,24 @@ $.widget("ui.sortable", $.ui.mouse, {
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,
@@ -4153,22 +4504,25 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -4176,19 +4530,20 @@ $.widget("ui.sortable", $.ui.mouse, {
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.
@@ -4197,36 +4552,44 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -4234,7 +4597,7 @@ $.widget("ui.sortable", $.ui.mouse, {
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
@@ -4242,11 +4605,13 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -4255,37 +4620,50 @@ $.widget("ui.sortable", $.ui.mouse, {
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;
@@ -4293,29 +4671,47 @@ $.widget("ui.sortable", $.ui.mouse, {
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
 
@@ -4330,7498 +4726,10262 @@ $.widget("ui.sortable", $.ui.mouse, {
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-
1
+ /*! jQuery UI - v1.10.2 - 2013-03-14
2
+ * http://jqueryui.com
3
+ * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.effect.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js, jquery.ui.menu.js, jquery.ui.position.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js
4
+ * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
5
+ (function( $, undefined ) {
 
 
 
 
 
6
 
7
+ var uuid = 0,
8
+ runiqueId = /^ui-id-\d+$/;
9
+
10
+ // $.ui might exist from components with no dependencies, e.g., $.ui.position
11
  $.ui = $.ui || {};
 
 
 
12
 
13
  $.extend( $.ui, {
14
+ version: "1.10.2",
15
 
16
  keyCode: {
 
17
  BACKSPACE: 8,
 
18
  COMMA: 188,
 
 
 
 
19
  DELETE: 46,
20
  DOWN: 40,
21
  END: 35,
22
  ENTER: 13,
23
  ESCAPE: 27,
24
  HOME: 36,
 
25
  LEFT: 37,
 
26
  NUMPAD_ADD: 107,
27
  NUMPAD_DECIMAL: 110,
28
  NUMPAD_DIVIDE: 111,
33
  PAGE_UP: 33,
34
  PERIOD: 190,
35
  RIGHT: 39,
 
36
  SPACE: 32,
37
  TAB: 9,
38
+ UP: 38
 
39
  }
40
  });
41
 
42
  // plugins
43
  $.fn.extend({
44
+ focus: (function( orig ) {
45
+ return function( delay, fn ) {
46
+ return typeof delay === "number" ?
47
+ this.each(function() {
48
+ var elem = this;
49
+ setTimeout(function() {
50
+ $( elem ).focus();
51
+ if ( fn ) {
52
+ fn.call( elem );
53
+ }
54
+ }, delay );
55
+ }) :
56
+ orig.apply( this, arguments );
57
+ };
58
+ })( $.fn.focus ),
 
59
 
60
  scrollParent: function() {
61
  var scrollParent;
62
+ if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
63
  scrollParent = this.parents().filter(function() {
64
+ return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
65
  }).eq(0);
66
  } else {
67
  scrollParent = this.parents().filter(function() {
68
+ return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
69
  }).eq(0);
70
  }
71
 
72
+ return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
73
  },
74
 
75
  zIndex: function( zIndex ) {
101
  return 0;
102
  },
103
 
104
+ uniqueId: function() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  return this.each(function() {
106
+ if ( !this.id ) {
107
+ this.id = "ui-id-" + (++uuid);
108
+ }
109
  });
110
+ },
 
 
 
 
 
111
 
112
+ removeUniqueId: function() {
113
  return this.each(function() {
114
+ if ( runiqueId.test( this.id ) ) {
115
+ $( this ).removeAttr( "id" );
116
+ }
117
  });
118
+ }
119
  });
120
 
121
  // selectors
122
  function focusable( element, isTabIndexNotNaN ) {
123
+ var map, mapName, img,
124
+ nodeName = element.nodeName.toLowerCase();
125
  if ( "area" === nodeName ) {
126
+ map = element.parentNode;
127
+ mapName = map.name;
 
128
  if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
129
  return false;
130
  }
131
  img = $( "img[usemap=#" + mapName + "]" )[0];
132
  return !!img && visible( img );
133
  }
134
+ return ( /input|select|textarea|button|object/.test( nodeName ) ?
135
+ !element.disabled :
136
+ "a" === nodeName ?
137
+ element.href || isTabIndexNotNaN :
138
+ isTabIndexNotNaN) &&
139
  // the element and all of its ancestors must be visible
140
+ visible( element );
141
  }
142
 
143
  function visible( element ) {
144
+ return $.expr.filters.visible( element ) &&
145
+ !$( element ).parents().addBack().filter(function() {
146
+ return $.css( this, "visibility" ) === "hidden";
147
+ }).length;
148
  }
149
 
150
  $.extend( $.expr[ ":" ], {
151
+ data: $.expr.createPseudo ?
152
+ $.expr.createPseudo(function( dataName ) {
153
+ return function( elem ) {
154
+ return !!$.data( elem, dataName );
155
+ };
156
+ }) :
157
+ // support: jQuery <1.8
158
+ function( elem, i, match ) {
159
+ return !!$.data( elem, match[ 3 ] );
160
+ },
161
 
162
  focusable: function( element ) {
163
  return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
170
  }
171
  });
172
 
173
+ // support: jQuery <1.8
174
+ if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
175
+ $.each( [ "Width", "Height" ], function( i, name ) {
176
+ var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
177
+ type = name.toLowerCase(),
178
+ orig = {
179
+ innerWidth: $.fn.innerWidth,
180
+ innerHeight: $.fn.innerHeight,
181
+ outerWidth: $.fn.outerWidth,
182
+ outerHeight: $.fn.outerHeight
183
+ };
184
+
185
+ function reduce( elem, size, border, margin ) {
186
+ $.each( side, function() {
187
+ size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
188
+ if ( border ) {
189
+ size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
190
+ }
191
+ if ( margin ) {
192
+ size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
193
+ }
194
+ });
195
+ return size;
196
+ }
197
+
198
+ $.fn[ "inner" + name ] = function( size ) {
199
+ if ( size === undefined ) {
200
+ return orig[ "inner" + name ].call( this );
201
+ }
202
 
203
+ return this.each(function() {
204
+ $( this ).css( type, reduce( this, size ) + "px" );
205
+ });
206
+ };
207
+
208
+ $.fn[ "outer" + name] = function( size, margin ) {
209
+ if ( typeof size !== "number" ) {
210
+ return orig[ "outer" + name ].call( this, size );
211
+ }
212
+
213
+ return this.each(function() {
214
+ $( this).css( type, reduce( this, size, true, margin ) + "px" );
215
+ });
216
+ };
217
  });
218
+ }
219
 
220
+ // support: jQuery <1.8
221
+ if ( !$.fn.addBack ) {
222
+ $.fn.addBack = function( selector ) {
223
+ return this.add( selector == null ?
224
+ this.prevObject : this.prevObject.filter( selector )
225
+ );
226
+ };
227
+ }
228
 
229
+ // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
230
+ if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
231
+ $.fn.removeData = (function( removeData ) {
232
+ return function( key ) {
233
+ if ( arguments.length ) {
234
+ return removeData.call( this, $.camelCase( key ) );
235
+ } else {
236
+ return removeData.call( this );
237
+ }
238
+ };
239
+ })( $.fn.removeData );
240
+ }
241
 
242
 
243
 
244
 
245
 
246
  // deprecated
247
+ $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
248
+
249
+ $.support.selectstart = "onselectstart" in document.createElement( "div" );
250
+ $.fn.extend({
251
+ disableSelection: function() {
252
+ return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
253
+ ".ui-disableSelection", function( event ) {
254
+ event.preventDefault();
255
+ });
256
+ },
257
+
258
+ enableSelection: function() {
259
+ return this.unbind( ".ui-disableSelection" );
260
+ }
261
+ });
262
+
263
  $.extend( $.ui, {
264
  // $.ui.plugin is deprecated. Use the proxy pattern instead.
265
  plugin: {
266
  add: function( module, option, set ) {
267
+ var i,
268
+ proto = $.ui[ module ].prototype;
269
+ for ( i in set ) {
270
  proto.plugins[ i ] = proto.plugins[ i ] || [];
271
  proto.plugins[ i ].push( [ option, set[ i ] ] );
272
  }
273
  },
274
  call: function( instance, name, args ) {
275
+ var i,
276
+ set = instance.plugins[ name ];
277
+ if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
278
  return;
279
  }
280
+
281
+ for ( i = 0; i < set.length; i++ ) {
282
  if ( instance.options[ set[ i ][ 0 ] ] ) {
283
  set[ i ][ 1 ].apply( instance.element, args );
284
  }
285
  }
286
  }
287
  },
288
+
 
 
 
 
 
 
 
289
  // only used by resizable
290
  hasScroll: function( el, a ) {
291
+
292
  //If overflow is hidden, the element might have extra content, but the user wants to hide it
293
  if ( $( el ).css( "overflow" ) === "hidden") {
294
  return false;
295
  }
296
+
297
  var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
298
  has = false;
299
+
300
  if ( el[ scroll ] > 0 ) {
301
  return true;
302
  }
303
+
304
  // TODO: determine which cases actually cause this to happen
305
  // if the element doesn't have the scroll set, see if it's possible to
306
  // set the scroll
308
  has = ( el[ scroll ] > 0 );
309
  el[ scroll ] = 0;
310
  return has;
 
 
 
 
 
 
 
 
 
 
311
  }
312
  });
313
 
314
  })( jQuery );
315
+
 
 
 
 
 
 
 
 
316
  (function( $, undefined ) {
317
 
318
+ var uuid = 0,
319
+ slice = Array.prototype.slice,
320
+ _cleanData = $.cleanData;
321
+ $.cleanData = function( elems ) {
322
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
323
+ try {
324
+ $( elem ).triggerHandler( "remove" );
325
+ // http://bugs.jquery.com/ticket/8235
326
+ } catch( e ) {}
327
+ }
328
+ _cleanData( elems );
329
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
331
  $.widget = function( name, base, prototype ) {
332
+ var fullName, existingConstructor, constructor, basePrototype,
333
+ // proxiedPrototype allows the provided prototype to remain unmodified
334
+ // so that it can be used as a mixin for multiple widgets (#8876)
335
+ proxiedPrototype = {},
336
+ namespace = name.split( "." )[ 0 ];
337
+
338
  name = name.split( "." )[ 1 ];
339
  fullName = namespace + "-" + name;
340
 
344
  }
345
 
346
  // create selector for plugin
347
+ $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
348
+ return !!$.data( elem, fullName );
349
  };
350
 
351
  $[ namespace ] = $[ namespace ] || {};
352
+ existingConstructor = $[ namespace ][ name ];
353
+ constructor = $[ namespace ][ name ] = function( options, element ) {
354
+ // allow instantiation without "new" keyword
355
+ if ( !this._createWidget ) {
356
+ return new constructor( options, element );
357
+ }
358
+
359
  // allow instantiation without initializing for simple inheritance
360
+ // must use "new" keyword (the code above always passes args)
361
  if ( arguments.length ) {
362
  this._createWidget( options, element );
363
  }
364
  };
365
+ // extend with the existing constructor to carry over any static properties
366
+ $.extend( constructor, existingConstructor, {
367
+ version: prototype.version,
368
+ // copy the object used to create the prototype in case we need to
369
+ // redefine the widget later
370
+ _proto: $.extend( {}, prototype ),
371
+ // track widgets that inherit from this widget in case this widget is
372
+ // redefined after a widget inherits from it
373
+ _childConstructors: []
374
+ });
375
 
376
+ basePrototype = new base();
377
  // we need to make the options hash a property directly on the new instance
378
  // otherwise we'll modify the options hash on the prototype that we're
379
  // inheriting from
380
+ basePrototype.options = $.widget.extend( {}, basePrototype.options );
381
+ $.each( prototype, function( prop, value ) {
382
+ if ( !$.isFunction( value ) ) {
383
+ proxiedPrototype[ prop ] = value;
384
+ return;
385
+ }
386
+ proxiedPrototype[ prop ] = (function() {
387
+ var _super = function() {
388
+ return base.prototype[ prop ].apply( this, arguments );
389
+ },
390
+ _superApply = function( args ) {
391
+ return base.prototype[ prop ].apply( this, args );
392
+ };
393
+ return function() {
394
+ var __super = this._super,
395
+ __superApply = this._superApply,
396
+ returnValue;
397
+
398
+ this._super = _super;
399
+ this._superApply = _superApply;
400
+
401
+ returnValue = value.apply( this, arguments );
402
+
403
+ this._super = __super;
404
+ this._superApply = __superApply;
405
+
406
+ return returnValue;
407
+ };
408
+ })();
409
+ });
410
+ constructor.prototype = $.widget.extend( basePrototype, {
411
+ // TODO: remove support for widgetEventPrefix
412
+ // always use the name + a colon as the prefix, e.g., draggable:start
413
+ // don't prefix for widgets that aren't DOM-based
414
+ widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
415
+ }, proxiedPrototype, {
416
+ constructor: constructor,
417
  namespace: namespace,
418
  widgetName: name,
419
+ widgetFullName: fullName
420
+ });
421
+
422
+ // If this widget is being redefined then we need to find all widgets that
423
+ // are inheriting from it and redefine all of them so that they inherit from
424
+ // the new version of this widget. We're essentially trying to replace one
425
+ // level in the prototype chain.
426
+ if ( existingConstructor ) {
427
+ $.each( existingConstructor._childConstructors, function( i, child ) {
428
+ var childPrototype = child.prototype;
429
+
430
+ // redefine the child widget using the same prototype that was
431
+ // originally used, but inherit from the new version of the base
432
+ $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
433
+ });
434
+ // remove the list of existing child constructors from the old constructor
435
+ // so the old child constructors can be garbage collected
436
+ delete existingConstructor._childConstructors;
437
+ } else {
438
+ base._childConstructors.push( constructor );
439
+ }
440
+
441
+ $.widget.bridge( name, constructor );
442
+ };
443
 
444
+ $.widget.extend = function( target ) {
445
+ var input = slice.call( arguments, 1 ),
446
+ inputIndex = 0,
447
+ inputLength = input.length,
448
+ key,
449
+ value;
450
+ for ( ; inputIndex < inputLength; inputIndex++ ) {
451
+ for ( key in input[ inputIndex ] ) {
452
+ value = input[ inputIndex ][ key ];
453
+ if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
454
+ // Clone objects
455
+ if ( $.isPlainObject( value ) ) {
456
+ target[ key ] = $.isPlainObject( target[ key ] ) ?
457
+ $.widget.extend( {}, target[ key ], value ) :
458
+ // Don't extend strings, arrays, etc. with objects
459
+ $.widget.extend( {}, value );
460
+ // Copy everything else by reference
461
+ } else {
462
+ target[ key ] = value;
463
+ }
464
+ }
465
+ }
466
+ }
467
+ return target;
468
  };
469
 
470
  $.widget.bridge = function( name, object ) {
471
+ var fullName = object.prototype.widgetFullName || name;
472
  $.fn[ name ] = function( options ) {
473
  var isMethodCall = typeof options === "string",
474
+ args = slice.call( arguments, 1 ),
475
  returnValue = this;
476
 
477
  // allow multiple hashes to be passed on init
478
  options = !isMethodCall && args.length ?
479
+ $.widget.extend.apply( null, [ options ].concat(args) ) :
480
  options;
481
 
 
 
 
 
 
482
  if ( isMethodCall ) {
483
  this.each(function() {
484
+ var methodValue,
485
+ instance = $.data( this, fullName );
486
+ if ( !instance ) {
487
+ return $.error( "cannot call methods on " + name + " prior to initialization; " +
488
+ "attempted to call method '" + options + "'" );
489
+ }
490
+ if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
491
+ return $.error( "no such method '" + options + "' for " + name + " widget instance" );
492
+ }
493
+ methodValue = instance[ options ].apply( instance, args );
 
 
 
494
  if ( methodValue !== instance && methodValue !== undefined ) {
495
+ returnValue = methodValue && methodValue.jquery ?
496
+ returnValue.pushStack( methodValue.get() ) :
497
+ methodValue;
498
  return false;
499
  }
500
  });
501
  } else {
502
  this.each(function() {
503
+ var instance = $.data( this, fullName );
504
  if ( instance ) {
505
  instance.option( options || {} )._init();
506
  } else {
507
+ $.data( this, fullName, new object( options, this ) );
508
  }
509
  });
510
  }
513
  };
514
  };
515
 
516
+ $.Widget = function( /* options, element */ ) {};
517
+ $.Widget._childConstructors = [];
 
 
 
 
518
 
519
  $.Widget.prototype = {
520
  widgetName: "widget",
521
  widgetEventPrefix: "",
522
+ defaultElement: "<div>",
523
  options: {
524
+ disabled: false,
525
+
526
+ // callbacks
527
+ create: null
528
  },
529
  _createWidget: function( options, element ) {
530
+ element = $( element || this.defaultElement || this )[ 0 ];
 
 
531
  this.element = $( element );
532
+ this.uuid = uuid++;
533
+ this.eventNamespace = "." + this.widgetName + this.uuid;
534
+ this.options = $.widget.extend( {},
535
  this.options,
536
  this._getCreateOptions(),
537
  options );
538
 
539
+ this.bindings = $();
540
+ this.hoverable = $();
541
+ this.focusable = $();
542
+
543
+ if ( element !== this ) {
544
+ $.data( element, this.widgetFullName, this );
545
+ this._on( true, this.element, {
546
+ remove: function( event ) {
547
+ if ( event.target === element ) {
548
+ this.destroy();
549
+ }
550
+ }
551
+ });
552
+ this.document = $( element.style ?
553
+ // element within the document
554
+ element.ownerDocument :
555
+ // element is window or document
556
+ element.document || element );
557
+ this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
558
+ }
559
 
560
  this._create();
561
+ this._trigger( "create", null, this._getCreateEventData() );
562
  this._init();
563
  },
564
+ _getCreateOptions: $.noop,
565
+ _getCreateEventData: $.noop,
566
+ _create: $.noop,
567
+ _init: $.noop,
 
568
 
569
  destroy: function() {
570
+ this._destroy();
571
+ // we can probably remove the unbind calls in 2.0
572
+ // all event bindings should go through this._on()
573
  this.element
574
+ .unbind( this.eventNamespace )
575
+ // 1.9 BC for #7810
576
+ // TODO remove dual storage
577
+ .removeData( this.widgetName )
578
+ .removeData( this.widgetFullName )
579
+ // support: jquery <1.6.3
580
+ // http://bugs.jquery.com/ticket/9413
581
+ .removeData( $.camelCase( this.widgetFullName ) );
582
  this.widget()
583
+ .unbind( this.eventNamespace )
584
  .removeAttr( "aria-disabled" )
585
  .removeClass(
586
+ this.widgetFullName + "-disabled " +
587
  "ui-state-disabled" );
588
+
589
+ // clean up events and states
590
+ this.bindings.unbind( this.eventNamespace );
591
+ this.hoverable.removeClass( "ui-state-hover" );
592
+ this.focusable.removeClass( "ui-state-focus" );
593
  },
594
+ _destroy: $.noop,
595
 
596
  widget: function() {
597
  return this.element;
598
  },
599
 
600
  option: function( key, value ) {
601
+ var options = key,
602
+ parts,
603
+ curOption,
604
+ i;
605
 
606
  if ( arguments.length === 0 ) {
607
  // don't return a reference to the internal hash
608
+ return $.widget.extend( {}, this.options );
609
  }
610
 
611
+ if ( typeof key === "string" ) {
612
+ // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
 
 
613
  options = {};
614
+ parts = key.split( "." );
615
+ key = parts.shift();
616
+ if ( parts.length ) {
617
+ curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
618
+ for ( i = 0; i < parts.length - 1; i++ ) {
619
+ curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
620
+ curOption = curOption[ parts[ i ] ];
621
+ }
622
+ key = parts.pop();
623
+ if ( value === undefined ) {
624
+ return curOption[ key ] === undefined ? null : curOption[ key ];
625
+ }
626
+ curOption[ key ] = value;
627
+ } else {
628
+ if ( value === undefined ) {
629
+ return this.options[ key ] === undefined ? null : this.options[ key ];
630
+ }
631
+ options[ key ] = value;
632
+ }
633
  }
634
 
635
  this._setOptions( options );
637
  return this;
638
  },
639
  _setOptions: function( options ) {
640
+ var key;
641
+
642
+ for ( key in options ) {
643
+ this._setOption( key, options[ key ] );
644
+ }
645
 
646
  return this;
647
  },
650
 
651
  if ( key === "disabled" ) {
652
  this.widget()
653
+ .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
 
 
654
  .attr( "aria-disabled", value );
655
+ this.hoverable.removeClass( "ui-state-hover" );
656
+ this.focusable.removeClass( "ui-state-focus" );
657
  }
658
 
659
  return this;
666
  return this._setOption( "disabled", true );
667
  },
668
 
669
+ _on: function( suppressDisabledCheck, element, handlers ) {
670
+ var delegateElement,
671
+ instance = this;
672
+
673
+ // no suppressDisabledCheck flag, shuffle arguments
674
+ if ( typeof suppressDisabledCheck !== "boolean" ) {
675
+ handlers = element;
676
+ element = suppressDisabledCheck;
677
+ suppressDisabledCheck = false;
678
+ }
679
+
680
+ // no element argument, shuffle and use this.element
681
+ if ( !handlers ) {
682
+ handlers = element;
683
+ element = this.element;
684
+ delegateElement = this.widget();
685
+ } else {
686
+ // accept selectors, DOM elements
687
+ element = delegateElement = $( element );
688
+ this.bindings = this.bindings.add( element );
689
+ }
690
+
691
+ $.each( handlers, function( event, handler ) {
692
+ function handlerProxy() {
693
+ // allow widgets to customize the disabled handling
694
+ // - disabled as an array instead of boolean
695
+ // - disabled class as method for disabling individual parts
696
+ if ( !suppressDisabledCheck &&
697
+ ( instance.options.disabled === true ||
698
+ $( this ).hasClass( "ui-state-disabled" ) ) ) {
699
+ return;
700
+ }
701
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
702
+ .apply( instance, arguments );
703
+ }
704
+
705
+ // copy the guid so direct unbinding works
706
+ if ( typeof handler !== "string" ) {
707
+ handlerProxy.guid = handler.guid =
708
+ handler.guid || handlerProxy.guid || $.guid++;
709
+ }
710
+
711
+ var match = event.match( /^(\w+)\s*(.*)$/ ),
712
+ eventName = match[1] + instance.eventNamespace,
713
+ selector = match[2];
714
+ if ( selector ) {
715
+ delegateElement.delegate( selector, eventName, handlerProxy );
716
+ } else {
717
+ element.bind( eventName, handlerProxy );
718
+ }
719
+ });
720
+ },
721
+
722
+ _off: function( element, eventName ) {
723
+ eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
724
+ element.unbind( eventName ).undelegate( eventName );
725
+ },
726
+
727
+ _delay: function( handler, delay ) {
728
+ function handlerProxy() {
729
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
730
+ .apply( instance, arguments );
731
+ }
732
+ var instance = this;
733
+ return setTimeout( handlerProxy, delay || 0 );
734
+ },
735
+
736
+ _hoverable: function( element ) {
737
+ this.hoverable = this.hoverable.add( element );
738
+ this._on( element, {
739
+ mouseenter: function( event ) {
740
+ $( event.currentTarget ).addClass( "ui-state-hover" );
741
+ },
742
+ mouseleave: function( event ) {
743
+ $( event.currentTarget ).removeClass( "ui-state-hover" );
744
+ }
745
+ });
746
+ },
747
+
748
+ _focusable: function( element ) {
749
+ this.focusable = this.focusable.add( element );
750
+ this._on( element, {
751
+ focusin: function( event ) {
752
+ $( event.currentTarget ).addClass( "ui-state-focus" );
753
+ },
754
+ focusout: function( event ) {
755
+ $( event.currentTarget ).removeClass( "ui-state-focus" );
756
+ }
757
+ });
758
+ },
759
+
760
  _trigger: function( type, event, data ) {
761
  var prop, orig,
762
  callback = this.options[ type ];
781
  }
782
 
783
  this.element.trigger( event, data );
784
+ return !( $.isFunction( callback ) &&
785
+ callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
 
786
  event.isDefaultPrevented() );
787
  }
788
  };
789
 
790
+ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
791
+ $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
792
+ if ( typeof options === "string" ) {
793
+ options = { effect: options };
794
+ }
795
+ var hasOptions,
796
+ effectName = !options ?
797
+ method :
798
+ options === true || typeof options === "number" ?
799
+ defaultEffect :
800
+ options.effect || defaultEffect;
801
+ options = options || {};
802
+ if ( typeof options === "number" ) {
803
+ options = { duration: options };
804
+ }
805
+ hasOptions = !$.isEmptyObject( options );
806
+ options.complete = callback;
807
+ if ( options.delay ) {
808
+ element.delay( options.delay );
809
+ }
810
+ if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
811
+ element[ method ]( options );
812
+ } else if ( effectName !== method && element[ effectName ] ) {
813
+ element[ effectName ]( options.duration, options.easing, callback );
814
+ } else {
815
+ element.queue(function( next ) {
816
+ $( this )[ method ]();
817
+ if ( callback ) {
818
+ callback.call( element[ 0 ] );
819
+ }
820
+ next();
821
+ });
822
+ }
823
+ };
824
+ });
825
+
826
  })( jQuery );
827
+
 
 
 
 
 
 
 
 
 
 
 
828
  (function( $, undefined ) {
829
 
830
  var mouseHandled = false;
831
+ $( document ).mouseup( function() {
832
  mouseHandled = false;
833
  });
834
 
835
  $.widget("ui.mouse", {
836
+ version: "1.10.2",
837
  options: {
838
+ cancel: "input,textarea,button,select,option",
839
  distance: 1,
840
  delay: 0
841
  },
842
  _mouseInit: function() {
843
+ var that = this;
844
 
845
  this.element
846
+ .bind("mousedown."+this.widgetName, function(event) {
847
+ return that._mouseDown(event);
848
  })
849
+ .bind("click."+this.widgetName, function(event) {
850
+ if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
851
+ $.removeData(event.target, that.widgetName + ".preventClickEvent");
852
  event.stopImmediatePropagation();
853
  return false;
854
  }
860
  // TODO: make sure destroying one instance of mouse doesn't mess with
861
  // other instances of mouse
862
  _mouseDestroy: function() {
863
+ this.element.unbind("."+this.widgetName);
864
+ if ( this._mouseMoveDelegate ) {
865
+ $(document)
866
+ .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
867
+ .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
868
+ }
869
  },
870
 
871
  _mouseDown: function(event) {
872
  // don't let more than one widget handle mouseStart
873
+ if( mouseHandled ) { return; }
874
 
875
  // we may have missed mouseup (out of window)
876
  (this._mouseStarted && this._mouseUp(event));
877
 
878
  this._mouseDownEvent = event;
879
 
880
+ var that = this,
881
+ btnIsLeft = (event.which === 1),
882
  // event.target.nodeName works around a bug in IE 8 with
883
  // disabled inputs (#7620)
884
+ elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
885
  if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
886
  return true;
887
  }
889
  this.mouseDelayMet = !this.options.delay;
890
  if (!this.mouseDelayMet) {
891
  this._mouseDelayTimer = setTimeout(function() {
892
+ that.mouseDelayMet = true;
893
  }, this.options.delay);
894
  }
895
 
902
  }
903
 
904
  // Click event may never have fired (Gecko & Opera)
905
+ if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
906
+ $.removeData(event.target, this.widgetName + ".preventClickEvent");
907
  }
908
 
909
  // these delegates are required to keep context
910
  this._mouseMoveDelegate = function(event) {
911
+ return that._mouseMove(event);
912
  };
913
  this._mouseUpDelegate = function(event) {
914
+ return that._mouseUp(event);
915
  };
916
  $(document)
917
+ .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
918
+ .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
919
 
920
  event.preventDefault();
921
+
922
  mouseHandled = true;
923
  return true;
924
  },
925
 
926
  _mouseMove: function(event) {
927
  // IE mouseup check - mouseup happened when mouse was out of window
928
+ if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
929
  return this._mouseUp(event);
930
  }
931
 
945
 
946
  _mouseUp: function(event) {
947
  $(document)
948
+ .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
949
+ .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
950
 
951
  if (this._mouseStarted) {
952
  this._mouseStarted = false;
953
 
954
+ if (event.target === this._mouseDownEvent.target) {
955
+ $.data(event.target, this.widgetName + ".preventClickEvent", true);
956
  }
957
 
958
  this._mouseStop(event);
969
  );
970
  },
971
 
972
+ _mouseDelayMet: function(/* event */) {
973
  return this.mouseDelayMet;
974
  },
975
 
976
  // These are placeholder methods, to be overriden by extending plugin
977
+ _mouseStart: function(/* event */) {},
978
+ _mouseDrag: function(/* event */) {},
979
+ _mouseStop: function(/* event */) {},
980
+ _mouseCapture: function(/* event */) { return true; }
981
  });
982
 
983
  })(jQuery);
984
+
 
 
 
 
 
 
 
 
985
  (function( $, undefined ) {
986
 
987
+ $.widget("ui.draggable", $.ui.mouse, {
988
+ version: "1.10.2",
989
+ widgetEventPrefix: "drag",
990
+ options: {
991
+ addClasses: true,
992
+ appendTo: "parent",
993
+ axis: false,
994
+ connectToSortable: false,
995
+ containment: false,
996
+ cursor: "auto",
997
+ cursorAt: false,
998
+ grid: false,
999
+ handle: false,
1000
+ helper: "original",
1001
+ iframeFix: false,
1002
+ opacity: false,
1003
+ refreshPositions: false,
1004
+ revert: false,
1005
+ revertDuration: 500,
1006
+ scope: "default",
1007
+ scroll: true,
1008
+ scrollSensitivity: 20,
1009
+ scrollSpeed: 20,
1010
+ snap: false,
1011
+ snapMode: "both",
1012
+ snapTolerance: 20,
1013
+ stack: false,
1014
+ zIndex: false,
1015
 
1016
+ // callbacks
1017
+ drag: null,
1018
+ start: null,
1019
+ stop: null
1020
+ },
1021
+ _create: function() {
1022
 
1023
+ if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
1024
+ this.element[0].style.position = "relative";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1025
  }
1026
+ if (this.options.addClasses){
1027
+ this.element.addClass("ui-draggable");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1028
  }
1029
+ if (this.options.disabled){
1030
+ this.element.addClass("ui-draggable-disabled");
 
 
 
 
 
 
 
1031
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1032
 
1033
  this._mouseInit();
1034
 
1035
  },
1036
 
1037
+ _destroy: function() {
1038
+ this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
 
 
 
 
 
 
1039
  this._mouseDestroy();
 
 
1040
  },
1041
 
1042
  _mouseCapture: function(event) {
1044
  var o = this.options;
1045
 
1046
  // among others, prevent a drag on a resizable-handle
1047
+ if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1048
  return false;
1049
+ }
1050
 
1051
  //Quit if we're not on a valid handle
1052
  this.handle = this._getHandle(event);
1053
+ if (!this.handle) {
1054
  return false;
 
 
 
 
 
 
 
 
 
 
 
1055
  }
1056
 
1057
+ $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1058
+ $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
1059
+ .css({
1060
+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1061
+ position: "absolute", opacity: "0.001", zIndex: 1000
1062
+ })
1063
+ .css($(this).offset())
1064
+ .appendTo("body");
1065
+ });
1066
+
1067
  return true;
1068
 
1069
  },
1075
  //Create and append the visible helper
1076
  this.helper = this._createHelper(event);
1077
 
1078
+ this.helper.addClass("ui-draggable-dragging");
1079
+
1080
  //Cache the helper size
1081
  this._cacheHelperProportions();
1082
 
1083
  //If ddmanager is used for droppables, set the global draggable
1084
+ if($.ui.ddmanager) {
1085
  $.ui.ddmanager.current = this;
1086
+ }
1087
 
1088
  /*
1089
  * - Position generation -
1118
  this.originalPageX = event.pageX;
1119
  this.originalPageY = event.pageY;
1120
 
1121
+ //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1122
  (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1123
 
1124
  //Set a containment if given in the options
1125
+ if(o.containment) {
1126
  this._setContainment();
1127
+ }
1128
 
1129
  //Trigger event + callbacks
1130
  if(this._trigger("start", event) === false) {
1136
  this._cacheHelperProportions();
1137
 
1138
  //Prepare the droppable offsets
1139
+ if ($.ui.ddmanager && !o.dropBehaviour) {
1140
  $.ui.ddmanager.prepareOffsets(this, event);
1141
+ }
1142
+
1143
 
 
1144
  this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1145
+
1146
  //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1147
+ if ( $.ui.ddmanager ) {
1148
+ $.ui.ddmanager.dragStart(this, event);
1149
+ }
1150
+
1151
  return true;
1152
  },
1153
 
1160
  //Call plugins and callbacks and use the resulting position if something is returned
1161
  if (!noPropagation) {
1162
  var ui = this._uiHash();
1163
+ if(this._trigger("drag", event, ui) === false) {
1164
  this._mouseUp({});
1165
  return false;
1166
  }
1167
  this.position = ui.position;
1168
  }
1169
 
1170
+ if(!this.options.axis || this.options.axis !== "y") {
1171
+ this.helper[0].style.left = this.position.left+"px";
1172
+ }
1173
+ if(!this.options.axis || this.options.axis !== "x") {
1174
+ this.helper[0].style.top = this.position.top+"px";
1175
+ }
1176
+ if($.ui.ddmanager) {
1177
+ $.ui.ddmanager.drag(this, event);
1178
+ }
1179
 
1180
  return false;
1181
  },
1183
  _mouseStop: function(event) {
1184
 
1185
  //If we are using droppables, inform the manager about the drop
1186
+ var element,
1187
+ that = this,
1188
+ elementInDom = false,
1189
+ dropped = false;
1190
+ if ($.ui.ddmanager && !this.options.dropBehaviour) {
1191
  dropped = $.ui.ddmanager.drop(this, event);
1192
+ }
1193
 
1194
  //if a drop comes from outside (a sortable)
1195
  if(this.dropped) {
1196
  dropped = this.dropped;
1197
  this.dropped = false;
1198
  }
1199
+
1200
+ //if the original element is no longer in the DOM don't bother to continue (see #8269)
1201
+ element = this.element[0];
1202
+ while ( element && (element = element.parentNode) ) {
1203
+ if (element === document ) {
1204
+ elementInDom = true;
1205
+ }
1206
+ }
1207
+ if ( !elementInDom && this.options.helper === "original" ) {
1208
  return false;
1209
+ }
1210
 
1211
+ 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))) {
 
1212
  $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1213
+ if(that._trigger("stop", event) !== false) {
1214
+ that._clear();
1215
  }
1216
  });
1217
  } else {
1222
 
1223
  return false;
1224
  },
1225
+
1226
  _mouseUp: function(event) {
1227
+ //Remove frame helpers
1228
+ $("div.ui-draggable-iframeFix").each(function() {
1229
+ this.parentNode.removeChild(this);
1230
+ });
1231
+
 
1232
  //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1233
+ if( $.ui.ddmanager ) {
1234
+ $.ui.ddmanager.dragStop(this, event);
1235
+ }
1236
+
1237
  return $.ui.mouse.prototype._mouseUp.call(this, event);
1238
  },
1239
+
1240
  cancel: function() {
1241
+
1242
  if(this.helper.is(".ui-draggable-dragging")) {
1243
  this._mouseUp({});
1244
  } else {
1245
  this._clear();
1246
  }
1247
+
1248
  return this;
1249
+
1250
  },
1251
 
1252
  _getHandle: function(event) {
1253
+ return this.options.handle ?
1254
+ !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1255
+ true;
 
 
 
 
 
 
 
 
1256
  },
1257
 
1258
  _createHelper: function(event) {
1259
 
1260
+ var o = this.options,
1261
+ helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
1262
 
1263
+ if(!helper.parents("body").length) {
1264
+ helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1265
+ }
1266
 
1267
+ if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1268
  helper.css("position", "absolute");
1269
+ }
1270
 
1271
  return helper;
1272
 
1273
  },
1274
 
1275
  _adjustOffsetFromHelper: function(obj) {
1276
+ if (typeof obj === "string") {
1277
+ obj = obj.split(" ");
1278
  }
1279
  if ($.isArray(obj)) {
1280
  obj = {left: +obj[0], top: +obj[1] || 0};
1281
  }
1282
+ if ("left" in obj) {
1283
  this.offset.click.left = obj.left + this.margins.left;
1284
  }
1285
+ if ("right" in obj) {
1286
  this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1287
  }
1288
+ if ("top" in obj) {
1289
  this.offset.click.top = obj.top + this.margins.top;
1290
  }
1291
+ if ("bottom" in obj) {
1292
  this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1293
  }
1294
  },
1303
  // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1304
  // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1305
  // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1306
+ if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1307
  po.left += this.scrollParent.scrollLeft();
1308
  po.top += this.scrollParent.scrollTop();
1309
  }
1310
 
1311
+ //This needs to be actually done for all browsers, since pageX/pageY includes this information
1312
+ //Ugly IE fix
1313
+ if((this.offsetParent[0] === document.body) ||
1314
+ (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
1315
  po = { top: 0, left: 0 };
1316
+ }
1317
 
1318
  return {
1319
  top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1324
 
1325
  _getRelativeOffset: function() {
1326
 
1327
+ if(this.cssPosition === "relative") {
1328
  var p = this.element.position();
1329
  return {
1330
  top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1354
 
1355
  _setContainment: function() {
1356
 
1357
+ var over, c, ce,
1358
+ o = this.options;
1359
+
1360
+ if(o.containment === "parent") {
1361
+ o.containment = this.helper[0].parentNode;
1362
+ }
1363
+ if(o.containment === "document" || o.containment === "window") {
1364
+ this.containment = [
1365
+ o.containment === "document" ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1366
+ o.containment === "document" ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1367
+ (o.containment === "document" ? 0 : $(window).scrollLeft()) + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
1368
+ (o.containment === "document" ? 0 : $(window).scrollTop()) + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1369
+ ];
1370
+ }
1371
 
1372
+ if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor !== Array) {
1373
+ c = $(o.containment);
1374
+ ce = c[0];
1375
+
1376
+ if(!ce) {
1377
+ return;
1378
+ }
1379
+
1380
+ over = ($(ce).css("overflow") !== "hidden");
1381
 
1382
  this.containment = [
1383
  (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
1384
  (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
1385
+ (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderRightWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
1386
+ (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderBottomWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
1387
  ];
1388
  this.relative_container = c;
1389
 
1390
+ } else if(o.containment.constructor === Array) {
1391
  this.containment = o.containment;
1392
  }
1393
 
1395
 
1396
  _convertPositionTo: function(d, pos) {
1397
 
1398
+ if(!pos) {
1399
+ pos = this.position;
1400
+ }
1401
+
1402
+ var mod = d === "absolute" ? 1 : -1,
1403
+ scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
1404
 
1405
  return {
1406
  top: (
1407
+ pos.top + // The absolute mouse position
1408
+ this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1409
+ this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
1410
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1411
  ),
1412
  left: (
1413
+ pos.left + // The absolute mouse position
1414
+ this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1415
+ this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
1416
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1417
  )
1418
  };
1419
 
1421
 
1422
  _generatePosition: function(event) {
1423
 
1424
+ var containment, co, top, left,
1425
+ o = this.options,
1426
+ scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
1427
+ scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName),
1428
+ pageX = event.pageX,
1429
+ pageY = event.pageY;
1430
 
1431
  /*
1432
  * - Position constraining -
1434
  */
1435
 
1436
  if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1437
+ if(this.containment) {
1438
+ if (this.relative_container){
1439
+ co = this.relative_container.offset();
1440
+ containment = [ this.containment[0] + co.left,
1441
+ this.containment[1] + co.top,
1442
+ this.containment[2] + co.left,
1443
+ this.containment[3] + co.top ];
1444
+ }
1445
+ else {
1446
+ containment = this.containment;
1447
+ }
1448
+
1449
+ if(event.pageX - this.offset.click.left < containment[0]) {
1450
+ pageX = containment[0] + this.offset.click.left;
1451
+ }
1452
+ if(event.pageY - this.offset.click.top < containment[1]) {
1453
+ pageY = containment[1] + this.offset.click.top;
1454
+ }
1455
+ if(event.pageX - this.offset.click.left > containment[2]) {
1456
+ pageX = containment[2] + this.offset.click.left;
1457
+ }
1458
+ if(event.pageY - this.offset.click.top > containment[3]) {
1459
+ pageY = containment[3] + this.offset.click.top;
1460
+ }
1461
  }
1462
 
1463
  if(o.grid) {
1464
  //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1465
+ top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1466
+ 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;
1467
 
1468
+ left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1469
+ 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;
1470
  }
1471
 
1472
  }
1473
 
1474
  return {
1475
  top: (
1476
+ pageY - // The absolute mouse position
1477
+ this.offset.click.top - // Click offset (relative to the element)
1478
+ this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
1479
+ this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
1480
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1481
  ),
1482
  left: (
1483
+ pageX - // The absolute mouse position
1484
+ this.offset.click.left - // Click offset (relative to the element)
1485
+ this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
1486
+ this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
1487
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1488
  )
1489
  };
1490
 
1492
 
1493
  _clear: function() {
1494
  this.helper.removeClass("ui-draggable-dragging");
1495
+ if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
1496
+ this.helper.remove();
1497
+ }
1498
  this.helper = null;
1499
  this.cancelHelperRemoval = false;
1500
  },
1504
  _trigger: function(type, event, ui) {
1505
  ui = ui || this._uiHash();
1506
  $.ui.plugin.call(this, type, [event, ui]);
1507
+ //The absolute position has to be recalculated after plugins
1508
+ if(type === "drag") {
1509
+ this.positionAbs = this._convertPositionTo("absolute");
1510
+ }
1511
  return $.Widget.prototype._trigger.call(this, type, event, ui);
1512
  },
1513
 
1514
  plugins: {},
1515
 
1516
+ _uiHash: function() {
1517
  return {
1518
  helper: this.helper,
1519
  position: this.position,
1524
 
1525
  });
1526
 
 
 
 
 
1527
  $.ui.plugin.add("draggable", "connectToSortable", {
1528
  start: function(event, ui) {
1529
 
1530
+ var inst = $(this).data("ui-draggable"), o = inst.options,
1531
  uiSortable = $.extend({}, ui, { item: inst.element });
1532
  inst.sortables = [];
1533
  $(o.connectToSortable).each(function() {
1534
+ var sortable = $.data(this, "ui-sortable");
1535
  if (sortable && !sortable.options.disabled) {
1536
  inst.sortables.push({
1537
  instance: sortable,
1546
  stop: function(event, ui) {
1547
 
1548
  //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1549
+ var inst = $(this).data("ui-draggable"),
1550
  uiSortable = $.extend({}, ui, { item: inst.element });
1551
 
1552
  $.each(inst.sortables, function() {
1557
  inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1558
  this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1559
 
1560
+ //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
1561
+ if(this.shouldRevert) {
1562
+ this.instance.options.revert = this.shouldRevert;
1563
+ }
1564
 
1565
  //Trigger the stop of the sortable
1566
  this.instance._mouseStop(event);
1568
  this.instance.options.helper = this.instance.options._helper;
1569
 
1570
  //If the helper has been the original item, restore properties in the sortable
1571
+ if(inst.options.helper === "original") {
1572
+ this.instance.currentItem.css({ top: "auto", left: "auto" });
1573
+ }
1574
 
1575
  } else {
1576
  this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1582
  },
1583
  drag: function(event, ui) {
1584
 
1585
+ var inst = $(this).data("ui-draggable"), that = this;
1586
 
1587
+ $.each(inst.sortables, function() {
 
 
 
 
1588
 
1589
+ var innermostIntersecting = false,
1590
+ thisSortable = this;
1591
 
 
 
1592
  //Copy over some variables to allow calling the sortable's native _intersectsWith
1593
  this.instance.positionAbs = inst.positionAbs;
1594
  this.instance.helperProportions = inst.helperProportions;
1595
  this.instance.offset.click = inst.offset.click;
1596
+
1597
  if(this.instance._intersectsWith(this.instance.containerCache)) {
1598
+ innermostIntersecting = true;
1599
+ $.each(inst.sortables, function () {
1600
+ this.instance.positionAbs = inst.positionAbs;
1601
+ this.instance.helperProportions = inst.helperProportions;
1602
+ this.instance.offset.click = inst.offset.click;
1603
+ if (this !== thisSortable &&
1604
+ this.instance._intersectsWith(this.instance.containerCache) &&
1605
+ $.contains(thisSortable.instance.element[0], this.instance.element[0])
1606
+ ) {
1607
+ innermostIntersecting = false;
1608
+ }
1609
+ return innermostIntersecting;
1610
+ });
1611
+ }
1612
 
1613
+
1614
+ if(innermostIntersecting) {
1615
  //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1616
  if(!this.instance.isOver) {
1617
 
1619
  //Now we fake the start of dragging for the sortable instance,
1620
  //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1621
  //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)
1622
+ this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
1623
  this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1624
  this.instance.options.helper = function() { return ui.helper[0]; };
1625
 
1642
  }
1643
 
1644
  //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
1645
+ if(this.instance.currentItem) {
1646
+ this.instance._mouseDrag(event);
1647
+ }
1648
 
1649
  } else {
1650
 
1654
 
1655
  this.instance.isOver = 0;
1656
  this.instance.cancelHelperRemoval = true;
1657
+
1658
  //Prevent reverting on this forced stop
1659
  this.instance.options.revert = false;
1660
+
1661
  // The out event needs to be triggered independently
1662
+ this.instance._trigger("out", event, this.instance._uiHash(this.instance));
1663
+
1664
  this.instance._mouseStop(event, true);
1665
  this.instance.options.helper = this.instance.options._helper;
1666
 
1667
  //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1668
  this.instance.currentItem.remove();
1669
+ if(this.instance.placeholder) {
1670
+ this.instance.placeholder.remove();
1671
+ }
1672
 
1673
  inst._trigger("fromSortable", event);
1674
  inst.dropped = false; //draggable revert needs that
1675
  }
1676
 
1677
+ }
1678
 
1679
  });
1680
 
1682
  });
1683
 
1684
  $.ui.plugin.add("draggable", "cursor", {
1685
+ start: function() {
1686
+ var t = $("body"), o = $(this).data("ui-draggable").options;
1687
+ if (t.css("cursor")) {
1688
+ o._cursor = t.css("cursor");
1689
+ }
1690
  t.css("cursor", o.cursor);
1691
  },
1692
+ stop: function() {
1693
+ var o = $(this).data("ui-draggable").options;
1694
+ if (o._cursor) {
1695
+ $("body").css("cursor", o._cursor);
1696
+ }
1697
  }
1698
  });
1699
 
1700
  $.ui.plugin.add("draggable", "opacity", {
1701
  start: function(event, ui) {
1702
+ var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1703
+ if(t.css("opacity")) {
1704
+ o._opacity = t.css("opacity");
1705
+ }
1706
+ t.css("opacity", o.opacity);
1707
  },
1708
  stop: function(event, ui) {
1709
+ var o = $(this).data("ui-draggable").options;
1710
+ if(o._opacity) {
1711
+ $(ui.helper).css("opacity", o._opacity);
1712
+ }
1713
  }
1714
  });
1715
 
1716
  $.ui.plugin.add("draggable", "scroll", {
1717
+ start: function() {
1718
+ var i = $(this).data("ui-draggable");
1719
+ if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1720
+ i.overflowOffset = i.scrollParent.offset();
1721
+ }
1722
  },
1723
+ drag: function( event ) {
1724
 
1725
+ var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
1726
 
1727
+ if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1728
 
1729
+ if(!o.axis || o.axis !== "x") {
1730
+ if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
1731
  i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1732
+ } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
1733
  i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1734
+ }
1735
  }
1736
 
1737
+ if(!o.axis || o.axis !== "y") {
1738
+ if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
1739
  i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1740
+ } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
1741
  i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1742
+ }
1743
  }
1744
 
1745
  } else {
1746
 
1747
+ if(!o.axis || o.axis !== "x") {
1748
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
1749
  scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1750
+ } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
1751
  scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1752
+ }
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
 
1765
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
1766
  $.ui.ddmanager.prepareOffsets(i, event);
1767
+ }
1768
 
1769
  }
1770
  });
1771
 
1772
  $.ui.plugin.add("draggable", "snap", {
1773
+ start: function() {
1774
+
1775
+ var i = $(this).data("ui-draggable"),
1776
+ o = i.options;
1777
 
 
1778
  i.snapElements = [];
1779
 
1780
+ $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
1781
+ var $t = $(this),
1782
+ $o = $t.offset();
1783
+ if(this !== i.element[0]) {
1784
+ i.snapElements.push({
1785
+ item: this,
1786
+ width: $t.outerWidth(), height: $t.outerHeight(),
1787
+ top: $o.top, left: $o.left
1788
+ });
1789
+ }
1790
  });
1791
 
1792
  },
1793
  drag: function(event, ui) {
1794
 
1795
+ var ts, bs, ls, rs, l, r, t, b, i, first,
1796
+ inst = $(this).data("ui-draggable"),
1797
+ o = inst.options,
1798
+ d = o.snapTolerance,
1799
+ x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1800
  y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1801
 
1802
+ for (i = inst.snapElements.length - 1; i >= 0; i--){
1803
 
1804
+ l = inst.snapElements[i].left;
1805
+ r = l + inst.snapElements[i].width;
1806
+ t = inst.snapElements[i].top;
1807
+ b = t + inst.snapElements[i].height;
1808
 
1809
  //Yes, I know, this is insane ;)
1810
  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))) {
1811
+ if(inst.snapElements[i].snapping) {
1812
+ (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1813
+ }
1814
  inst.snapElements[i].snapping = false;
1815
  continue;
1816
  }
1817
 
1818
+ if(o.snapMode !== "inner") {
1819
+ ts = Math.abs(t - y2) <= d;
1820
+ bs = Math.abs(b - y1) <= d;
1821
+ ls = Math.abs(l - x2) <= d;
1822
+ rs = Math.abs(r - x1) <= d;
1823
+ if(ts) {
1824
+ ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1825
+ }
1826
+ if(bs) {
1827
+ ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1828
+ }
1829
+ if(ls) {
1830
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1831
+ }
1832
+ if(rs) {
1833
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1834
+ }
1835
  }
1836
 
1837
+ first = (ts || bs || ls || rs);
1838
 
1839
+ if(o.snapMode !== "outer") {
1840
+ ts = Math.abs(t - y1) <= d;
1841
+ bs = Math.abs(b - y2) <= d;
1842
+ ls = Math.abs(l - x1) <= d;
1843
+ rs = Math.abs(r - x2) <= d;
1844
+ if(ts) {
1845
+ ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1846
+ }
1847
+ if(bs) {
1848
+ ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1849
+ }
1850
+ if(ls) {
1851
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1852
+ }
1853
+ if(rs) {
1854
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1855
+ }
1856
  }
1857
 
1858
+ if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
1859
  (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1860
+ }
1861
  inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1862
 
1863
+ }
1864
 
1865
  }
1866
  });
1867
 
1868
  $.ui.plugin.add("draggable", "stack", {
1869
+ start: function() {
1870
+ var min,
1871
+ o = this.data("ui-draggable").options,
1872
+ group = $.makeArray($(o.stack)).sort(function(a,b) {
1873
+ return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1874
+ });
1875
 
 
 
 
1876
  if (!group.length) { return; }
1877
+
1878
+ min = parseInt($(group[0]).css("zIndex"), 10) || 0;
1879
  $(group).each(function(i) {
1880
+ $(this).css("zIndex", min + i);
1881
  });
1882
+ this.css("zIndex", (min + group.length));
 
 
1883
  }
1884
  });
1885
 
1886
  $.ui.plugin.add("draggable", "zIndex", {
1887
  start: function(event, ui) {
1888
+ var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1889
+ if(t.css("zIndex")) {
1890
+ o._zIndex = t.css("zIndex");
1891
+ }
1892
+ t.css("zIndex", o.zIndex);
1893
  },
1894
  stop: function(event, ui) {
1895
+ var o = $(this).data("ui-draggable").options;
1896
+ if(o._zIndex) {
1897
+ $(ui.helper).css("zIndex", o._zIndex);
1898
+ }
1899
  }
1900
  });
1901
 
1902
  })(jQuery);
1903
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1904
  (function( $, undefined ) {
1905
 
1906
+ function isOverAxis( x, reference, size ) {
1907
+ return ( x > reference ) && ( x < ( reference + size ) );
1908
+ }
1909
+
1910
  $.widget("ui.droppable", {
1911
+ version: "1.10.2",
1912
  widgetEventPrefix: "drop",
1913
  options: {
1914
+ accept: "*",
1915
  activeClass: false,
1916
  addClasses: true,
1917
  greedy: false,
1918
  hoverClass: false,
1919
+ scope: "default",
1920
+ tolerance: "intersect",
1921
+
1922
+ // callbacks
1923
+ activate: null,
1924
+ deactivate: null,
1925
+ drop: null,
1926
+ out: null,
1927
+ over: null
1928
  },
1929
  _create: function() {
1930
 
1931
+ var o = this.options,
1932
+ accept = o.accept;
1933
+
1934
+ this.isover = false;
1935
+ this.isout = true;
1936
 
1937
  this.accept = $.isFunction(accept) ? accept : function(d) {
1938
  return d.is(accept);
1949
 
1950
  },
1951
 
1952
+ _destroy: function() {
1953
+ var i = 0,
1954
+ drop = $.ui.ddmanager.droppables[this.options.scope];
 
 
1955
 
1956
+ for ( ; i < drop.length; i++ ) {
1957
+ if ( drop[i] === this ) {
1958
+ drop.splice(i, 1);
1959
+ }
1960
+ }
1961
 
1962
+ this.element.removeClass("ui-droppable ui-droppable-disabled");
1963
  },
1964
 
1965
  _setOption: function(key, value) {
1966
 
1967
+ if(key === "accept") {
1968
  this.accept = $.isFunction(value) ? value : function(d) {
1969
  return d.is(value);
1970
  };
1974
 
1975
  _activate: function(event) {
1976
  var draggable = $.ui.ddmanager.current;
1977
+ if(this.options.activeClass) {
1978
+ this.element.addClass(this.options.activeClass);
1979
+ }
1980
+ if(draggable){
1981
+ this._trigger("activate", event, this.ui(draggable));
1982
+ }
1983
  },
1984
 
1985
  _deactivate: function(event) {
1986
  var draggable = $.ui.ddmanager.current;
1987
+ if(this.options.activeClass) {
1988
+ this.element.removeClass(this.options.activeClass);
1989
+ }
1990
+ if(draggable){
1991
+ this._trigger("deactivate", event, this.ui(draggable));
1992
+ }
1993
  },
1994
 
1995
  _over: function(event) {
1996
 
1997
  var draggable = $.ui.ddmanager.current;
1998
+
1999
+ // Bail if draggable and droppable are same element
2000
+ if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2001
+ return;
2002
+ }
2003
 
2004
  if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2005
+ if(this.options.hoverClass) {
2006
+ this.element.addClass(this.options.hoverClass);
2007
+ }
2008
+ this._trigger("over", event, this.ui(draggable));
2009
  }
2010
 
2011
  },
2013
  _out: function(event) {
2014
 
2015
  var draggable = $.ui.ddmanager.current;
2016
+
2017
+ // Bail if draggable and droppable are same element
2018
+ if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2019
+ return;
2020
+ }
2021
 
2022
  if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2023
+ if(this.options.hoverClass) {
2024
+ this.element.removeClass(this.options.hoverClass);
2025
+ }
2026
+ this._trigger("out", event, this.ui(draggable));
2027
  }
2028
 
2029
  },
2030
 
2031
  _drop: function(event,custom) {
2032
 
2033
+ var draggable = custom || $.ui.ddmanager.current,
2034
+ childrenIntersection = false;
2035
+
2036
+ // Bail if draggable and droppable are same element
2037
+ if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2038
+ return false;
2039
+ }
2040
 
2041
+ this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
2042
+ var inst = $.data(this, "ui-droppable");
 
2043
  if(
2044
+ inst.options.greedy &&
2045
+ !inst.options.disabled &&
2046
+ inst.options.scope === draggable.options.scope &&
2047
+ inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
2048
+ $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2049
  ) { childrenIntersection = true; return false; }
2050
  });
2051
+ if(childrenIntersection) {
2052
+ return false;
2053
+ }
2054
 
2055
  if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2056
+ if(this.options.activeClass) {
2057
+ this.element.removeClass(this.options.activeClass);
2058
+ }
2059
+ if(this.options.hoverClass) {
2060
+ this.element.removeClass(this.options.hoverClass);
2061
+ }
2062
+ this._trigger("drop", event, this.ui(draggable));
2063
  return this.element;
2064
  }
2065
 
2078
 
2079
  });
2080
 
 
 
 
 
2081
  $.ui.intersect = function(draggable, droppable, toleranceMode) {
2082
 
2083
+ if (!droppable.offset) {
2084
+ return false;
2085
+ }
2086
 
2087
+ var draggableLeft, draggableTop,
2088
+ x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2089
+ y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
2090
+ l = droppable.offset.left, r = l + droppable.proportions.width,
2091
  t = droppable.offset.top, b = t + droppable.proportions.height;
2092
 
2093
  switch (toleranceMode) {
2094
+ case "fit":
2095
+ return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
2096
+ case "intersect":
2097
+ return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
2098
+ x2 - (draggable.helperProportions.width / 2) < r && // Left Half
2099
+ t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
2100
+ y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2101
+ case "pointer":
2102
+ draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
2103
+ draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
2104
+ return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
2105
+ case "touch":
 
 
 
 
 
2106
  return (
2107
+ (y1 >= t && y1 <= b) || // Top edge touching
2108
+ (y2 >= t && y2 <= b) || // Bottom edge touching
2109
+ (y1 < t && y2 > b) // Surrounded vertically
2110
+ ) && (
2111
+ (x1 >= l && x1 <= r) || // Left edge touching
2112
+ (x2 >= l && x2 <= r) || // Right edge touching
2113
+ (x1 < l && x2 > r) // Surrounded horizontally
2114
+ );
 
2115
  default:
2116
  return false;
 
2117
  }
2118
 
2119
  };
2123
  */
2124
  $.ui.ddmanager = {
2125
  current: null,
2126
+ droppables: { "default": [] },
2127
  prepareOffsets: function(t, event) {
2128
 
2129
+ var i, j,
2130
+ m = $.ui.ddmanager.droppables[t.options.scope] || [],
2131
+ type = event ? event.type : null, // workaround for #2317
2132
+ list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
2133
+
2134
+ droppablesLoop: for (i = 0; i < m.length; i++) {
2135
 
2136
+ //No disabled and non-accepted
2137
+ if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
2138
+ continue;
2139
+ }
2140
+
2141
+ // Filter out elements in the current dragged item
2142
+ for (j=0; j < list.length; j++) {
2143
+ if(list[j] === m[i].element[0]) {
2144
+ m[i].proportions.height = 0;
2145
+ continue droppablesLoop;
2146
+ }
2147
+ }
2148
 
2149
+ m[i].visible = m[i].element.css("display") !== "none";
2150
+ if(!m[i].visible) {
2151
+ continue;
2152
+ }
2153
 
2154
+ //Activate the droppable if used directly from draggables
2155
+ if(type === "mousedown") {
2156
+ m[i]._activate.call(m[i], event);
2157
+ }
2158
 
2159
  m[i].offset = m[i].element.offset();
2160
  m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2165
  drop: function(draggable, event) {
2166
 
2167
  var dropped = false;
2168
+ // Create a copy of the droppables in case the list changes during the drop (#9116)
2169
+ $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
2170
 
2171
+ if(!this.options) {
2172
+ return;
2173
+ }
2174
+ if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
2175
  dropped = this._drop.call(this, event) || dropped;
2176
+ }
2177
 
2178
  if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2179
+ this.isout = true;
2180
+ this.isover = false;
2181
  this._deactivate.call(this, event);
2182
  }
2183
 
2187
  },
2188
  dragStart: function( draggable, event ) {
2189
  //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2190
+ draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2191
+ if( !draggable.options.refreshPositions ) {
2192
+ $.ui.ddmanager.prepareOffsets( draggable, event );
2193
+ }
2194
  });
2195
  },
2196
  drag: function(draggable, event) {
2197
 
2198
  //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2199
+ if(draggable.options.refreshPositions) {
2200
+ $.ui.ddmanager.prepareOffsets(draggable, event);
2201
+ }
2202
 
2203
  //Run through all droppables and check their positions based on specific tolerance options
2204
  $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2205
 
2206
+ if(this.options.disabled || this.greedyChild || !this.visible) {
2207
+ return;
2208
+ }
2209
 
2210
+ var parentInstance, scope, parent,
2211
+ intersects = $.ui.intersect(draggable, this, this.options.tolerance),
2212
+ c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
2213
+ if(!c) {
2214
+ return;
2215
+ }
2216
 
 
2217
  if (this.options.greedy) {
2218
+ // find droppable parents with same scope
2219
+ scope = this.options.scope;
2220
+ parent = this.element.parents(":data(ui-droppable)").filter(function () {
2221
+ return $.data(this, "ui-droppable").options.scope === scope;
2222
+ });
2223
+
2224
  if (parent.length) {
2225
+ parentInstance = $.data(parent[0], "ui-droppable");
2226
+ parentInstance.greedyChild = (c === "isover");
2227
  }
2228
  }
2229
 
2230
  // we just moved into a greedy child
2231
+ if (parentInstance && c === "isover") {
2232
+ parentInstance.isover = false;
2233
+ parentInstance.isout = true;
2234
  parentInstance._out.call(parentInstance, event);
2235
  }
2236
 
2237
+ this[c] = true;
2238
+ this[c === "isout" ? "isover" : "isout"] = false;
2239
+ this[c === "isover" ? "_over" : "_out"].call(this, event);
2240
 
2241
  // we just moved out of a greedy child
2242
+ if (parentInstance && c === "isout") {
2243
+ parentInstance.isout = false;
2244
+ parentInstance.isover = true;
2245
  parentInstance._over.call(parentInstance, event);
2246
  }
2247
  });
2248
 
2249
  },
2250
  dragStop: function( draggable, event ) {
2251
+ draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2252
  //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2253
+ if( !draggable.options.refreshPositions ) {
2254
+ $.ui.ddmanager.prepareOffsets( draggable, event );
2255
+ }
2256
  }
2257
  };
2258
 
2259
  })(jQuery);
2260
+
 
 
 
 
 
 
 
 
 
 
 
 
 
2261
  (function( $, undefined ) {
2262
 
2263
+ function num(v) {
2264
+ return parseInt(v, 10) || 0;
2265
+ }
2266
+
2267
+ function isNumber(value) {
2268
+ return !isNaN(parseInt(value, 10));
2269
+ }
2270
+
2271
  $.widget("ui.resizable", $.ui.mouse, {
2272
+ version: "1.10.2",
2273
  widgetEventPrefix: "resize",
2274
  options: {
2275
  alsoResize: false,
2287
  maxWidth: null,
2288
  minHeight: 10,
2289
  minWidth: 10,
2290
+ // See #7960
2291
+ zIndex: 90,
2292
+
2293
+ // callbacks
2294
+ resize: null,
2295
+ start: null,
2296
+ stop: null
2297
  },
2298
  _create: function() {
2299
 
2300
+ var n, i, handle, axis, hname,
2301
+ that = this,
2302
+ o = this.options;
2303
  this.element.addClass("ui-resizable");
2304
 
2305
  $.extend(this, {
2307
  aspectRatio: o.aspectRatio,
2308
  originalElement: this.element,
2309
  _proportionallyResizeElements: [],
2310
+ _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
2311
  });
2312
 
2313
  //Wrap the element if it cannot hold child nodes
2314
  if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2315
 
 
 
 
 
2316
  //Create a wrapper element and set the wrapper to the new current internal element
2317
  this.element.wrap(
2318
+ $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
2319
+ position: this.element.css("position"),
2320
  width: this.element.outerWidth(),
2321
  height: this.element.outerHeight(),
2322
+ top: this.element.css("top"),
2323
+ left: this.element.css("left")
2324
  })
2325
  );
2326
 
2327
  //Overwrite the original this.element
2328
  this.element = this.element.parent().data(
2329
+ "ui-resizable", this.element.data("ui-resizable")
2330
  );
2331
 
2332
  this.elementIsWrapper = true;
2336
  this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2337
 
2338
  //Prevent Safari textarea resize
2339
+ this.originalResizeStyle = this.originalElement.css("resize");
2340
+ this.originalElement.css("resize", "none");
2341
 
2342
  //Push the actual element to our proportionallyResize internal array
2343
+ this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
2344
 
2345
  // avoid IE jump (hard set the margin)
2346
+ this.originalElement.css({ margin: this.originalElement.css("margin") });
2347
 
2348
  // fix handlers offset
2349
  this._proportionallyResize();
2350
 
2351
  }
2352
 
2353
+ 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" });
2354
+ if(this.handles.constructor === String) {
2355
+
2356
+ if ( this.handles === "all") {
2357
+ this.handles = "n,e,s,w,se,sw,ne,nw";
2358
+ }
2359
 
2360
+ n = this.handles.split(",");
2361
+ this.handles = {};
2362
 
2363
+ for(i = 0; i < n.length; i++) {
2364
 
2365
+ handle = $.trim(n[i]);
2366
+ hname = "ui-resizable-"+handle;
2367
+ axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
2368
 
2369
+ // Apply zIndex to all handles - see #7960
2370
+ axis.css({ zIndex: o.zIndex });
 
2371
 
2372
  //TODO : What's going on here?
2373
+ if ("se" === handle) {
2374
+ axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
2375
+ }
2376
 
2377
  //Insert into internal handles object and append to element
2378
+ this.handles[handle] = ".ui-resizable-"+handle;
2379
  this.element.append(axis);
2380
  }
2381
 
2383
 
2384
  this._renderAxis = function(target) {
2385
 
2386
+ var i, axis, padPos, padWrapper;
2387
+
2388
  target = target || this.element;
2389
 
2390
+ for(i in this.handles) {
2391
 
2392
+ if(this.handles[i].constructor === String) {
2393
  this.handles[i] = $(this.handles[i], this.element).show();
2394
+ }
2395
 
2396
  //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2397
  if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2398
 
2399
+ axis = $(this.handles[i], this.element);
2400
 
2401
  //Checking the correct pad and border
2402
  padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2403
 
2404
  //The padding type i have to apply...
2405
+ padPos = [ "padding",
2406
+ /ne|nw|n/.test(i) ? "Top" :
2407
+ /se|sw|s/.test(i) ? "Bottom" :
2408
+ /^e$/.test(i) ? "Right" : "Left" ].join("");
2409
 
2410
  target.css(padPos, padWrapper);
2411
 
2414
  }
2415
 
2416
  //TODO: What's that good for? There's not anything to be executed left
2417
+ if(!$(this.handles[i]).length) {
2418
  continue;
2419
+ }
2420
  }
2421
  };
2422
 
2423
  //TODO: make renderAxis a prototype function
2424
  this._renderAxis(this.element);
2425
 
2426
+ this._handles = $(".ui-resizable-handle", this.element)
2427
  .disableSelection();
2428
 
2429
  //Matching axis name
2430
  this._handles.mouseover(function() {
2431
+ if (!that.resizing) {
2432
+ if (this.className) {
2433
+ axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2434
+ }
2435
  //Axis, default = se
2436
+ that.axis = axis && axis[1] ? axis[1] : "se";
2437
  }
2438
  });
2439
 
2442
  this._handles.hide();
2443
  $(this.element)
2444
  .addClass("ui-resizable-autohide")
2445
+ .mouseenter(function() {
2446
+ if (o.disabled) {
2447
+ return;
2448
+ }
2449
  $(this).removeClass("ui-resizable-autohide");
2450
+ that._handles.show();
2451
+ })
2452
+ .mouseleave(function(){
2453
+ if (o.disabled) {
2454
+ return;
2455
+ }
2456
+ if (!that.resizing) {
2457
  $(this).addClass("ui-resizable-autohide");
2458
+ that._handles.hide();
2459
  }
2460
  });
2461
  }
2465
 
2466
  },
2467
 
2468
+ _destroy: function() {
2469
 
2470
  this._mouseDestroy();
2471
 
2472
+ var wrapper,
2473
+ _destroy = function(exp) {
2474
+ $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2475
+ .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
2476
+ };
2477
 
2478
  //TODO: Unwrap at same DOM position
2479
  if (this.elementIsWrapper) {
2480
  _destroy(this.element);
2481
+ wrapper = this.element;
2482
+ this.originalElement.css({
2483
+ position: wrapper.css("position"),
2484
+ width: wrapper.outerWidth(),
2485
+ height: wrapper.outerHeight(),
2486
+ top: wrapper.css("top"),
2487
+ left: wrapper.css("left")
2488
+ }).insertAfter( wrapper );
2489
+ wrapper.remove();
2490
+ }
2491
+
2492
+ this.originalElement.css("resize", this.originalResizeStyle);
 
2493
  _destroy(this.originalElement);
2494
 
2495
  return this;
2496
  },
2497
 
2498
  _mouseCapture: function(event) {
2499
+ var i, handle,
2500
+ capture = false;
2501
+
2502
+ for (i in this.handles) {
2503
+ handle = $(this.handles[i])[0];
2504
+ if (handle === event.target || $.contains(handle, event.target)) {
2505
+ capture = true;
2506
  }
2507
  }
2508
 
2509
+ return !this.options.disabled && capture;
2510
  },
2511
 
2512
  _mouseStart: function(event) {
2513
 
2514
+ var curleft, curtop, cursor,
2515
+ o = this.options,
2516
+ iniPos = this.element.position(),
2517
+ el = this.element;
2518
 
2519
  this.resizing = true;
 
2520
 
2521
  // bugfix for http://dev.jquery.com/ticket/1749
2522
+ if ( (/absolute/).test( el.css("position") ) ) {
2523
+ el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
2524
+ } else if (el.is(".ui-draggable")) {
2525
+ el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
2526
  }
2527
 
 
 
 
 
2528
  this._renderProxy();
2529
 
2530
+ curleft = num(this.helper.css("left"));
2531
+ curtop = num(this.helper.css("top"));
2532
 
2533
  if (o.containment) {
2534
  curleft += $(o.containment).scrollLeft() || 0;
2545
  this.originalMousePosition = { left: event.pageX, top: event.pageY };
2546
 
2547
  //Aspect Ratio
2548
+ this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2549
 
2550
+ cursor = $(".ui-resizable-" + this.axis).css("cursor");
2551
+ $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
2552
 
2553
  el.addClass("ui-resizable-resizing");
2554
  this._propagate("start", event);
2558
  _mouseDrag: function(event) {
2559
 
2560
  //Increase performance, avoid regex
2561
+ var data,
2562
+ el = this.helper, props = {},
2563
+ smp = this.originalMousePosition,
2564
+ a = this.axis,
2565
+ prevTop = this.position.top,
2566
+ prevLeft = this.position.left,
2567
+ prevWidth = this.size.width,
2568
+ prevHeight = this.size.height,
2569
+ dx = (event.pageX-smp.left)||0,
2570
+ dy = (event.pageY-smp.top)||0,
2571
+ trigger = this._change[a];
2572
+
2573
+ if (!trigger) {
2574
+ return false;
2575
+ }
2576
 
2577
  // Calculate the attrs that will be change
2578
+ data = trigger.apply(this, [event, dx, dy]);
2579
 
2580
  // Put this in the mouseDrag handler since the user can start pressing shift while resizing
2581
  this._updateVirtualBoundaries(event.shiftKey);
2582
+ if (this._aspectRatio || event.shiftKey) {
2583
  data = this._updateRatio(data, event);
2584
+ }
2585
 
2586
  data = this._respectSize(data, event);
2587
 
2588
+ this._updateCache(data);
2589
+
2590
  // plugins callbacks need to be called first
2591
  this._propagate("resize", event);
2592
 
2593
+ if (this.position.top !== prevTop) {
2594
+ props.top = this.position.top + "px";
2595
+ }
2596
+ if (this.position.left !== prevLeft) {
2597
+ props.left = this.position.left + "px";
2598
+ }
2599
+ if (this.size.width !== prevWidth) {
2600
+ props.width = this.size.width + "px";
2601
+ }
2602
+ if (this.size.height !== prevHeight) {
2603
+ props.height = this.size.height + "px";
2604
+ }
2605
+ el.css(props);
2606
 
2607
+ if (!this._helper && this._proportionallyResizeElements.length) {
2608
  this._proportionallyResize();
2609
+ }
2610
 
2611
+ // Call the user callback if the element was resized
2612
+ if ( ! $.isEmptyObject(props) ) {
2613
+ this._trigger("resize", event, this.ui());
2614
+ }
2615
 
2616
  return false;
2617
  },
2619
  _mouseStop: function(event) {
2620
 
2621
  this.resizing = false;
2622
+ var pr, ista, soffseth, soffsetw, s, left, top,
2623
+ o = this.options, that = this;
2624
 
2625
  if(this._helper) {
 
 
 
2626
 
2627
+ pr = this._proportionallyResizeElements;
2628
+ ista = pr.length && (/textarea/i).test(pr[0].nodeName);
2629
+ soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
2630
+ soffsetw = ista ? 0 : that.sizeDiff.width;
2631
+
2632
+ s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
2633
+ left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
2634
+ top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2635
 
2636
+ if (!o.animate) {
2637
  this.element.css($.extend(s, { top: top, left: left }));
2638
+ }
2639
 
2640
+ that.helper.height(that.size.height);
2641
+ that.helper.width(that.size.width);
2642
 
2643
+ if (this._helper && !o.animate) {
2644
+ this._proportionallyResize();
2645
+ }
2646
  }
2647
 
2648
+ $("body").css("cursor", "auto");
2649
 
2650
  this.element.removeClass("ui-resizable-resizing");
2651
 
2652
  this._propagate("stop", event);
2653
 
2654
+ if (this._helper) {
2655
+ this.helper.remove();
2656
+ }
2657
+
2658
  return false;
2659
 
2660
  },
2661
 
2662
+ _updateVirtualBoundaries: function(forceAspectRatio) {
2663
+ var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
2664
+ o = this.options;
2665
 
2666
+ b = {
2667
+ minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
2668
+ maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
2669
+ minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
2670
+ maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
2671
+ };
2672
 
2673
+ if(this._aspectRatio || forceAspectRatio) {
2674
+ // We want to create an enclosing box whose aspect ration is the requested one
2675
+ // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
2676
+ pMinWidth = b.minHeight * this.aspectRatio;
2677
+ pMinHeight = b.minWidth / this.aspectRatio;
2678
+ pMaxWidth = b.maxHeight * this.aspectRatio;
2679
+ pMaxHeight = b.maxWidth / this.aspectRatio;
2680
 
2681
+ if(pMinWidth > b.minWidth) {
2682
+ b.minWidth = pMinWidth;
2683
+ }
2684
+ if(pMinHeight > b.minHeight) {
2685
+ b.minHeight = pMinHeight;
2686
+ }
2687
+ if(pMaxWidth < b.maxWidth) {
2688
+ b.maxWidth = pMaxWidth;
2689
+ }
2690
+ if(pMaxHeight < b.maxHeight) {
2691
+ b.maxHeight = pMaxHeight;
2692
+ }
2693
+ }
2694
+ this._vBoundaries = b;
2695
+ },
2696
 
2697
  _updateCache: function(data) {
 
2698
  this.offset = this.helper.offset();
2699
+ if (isNumber(data.left)) {
2700
+ this.position.left = data.left;
2701
+ }
2702
+ if (isNumber(data.top)) {
2703
+ this.position.top = data.top;
2704
+ }
2705
+ if (isNumber(data.height)) {
2706
+ this.size.height = data.height;
2707
+ }
2708
+ if (isNumber(data.width)) {
2709
+ this.size.width = data.width;
2710
+ }
2711
  },
2712
 
2713
+ _updateRatio: function( data ) {
2714
 
2715
+ var cpos = this.position,
2716
+ csize = this.size,
2717
+ a = this.axis;
2718
 
2719
+ if (isNumber(data.height)) {
2720
+ data.width = (data.height * this.aspectRatio);
2721
+ } else if (isNumber(data.width)) {
2722
+ data.height = (data.width / this.aspectRatio);
2723
+ }
2724
 
2725
+ if (a === "sw") {
2726
  data.left = cpos.left + (csize.width - data.width);
2727
  data.top = null;
2728
  }
2729
+ if (a === "nw") {
2730
  data.top = cpos.top + (csize.height - data.height);
2731
  data.left = cpos.left + (csize.width - data.width);
2732
  }
2734
  return data;
2735
  },
2736
 
2737
+ _respectSize: function( data ) {
2738
 
2739
+ var o = this._vBoundaries,
2740
+ a = this.axis,
2741
+ ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2742
+ isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
2743
+ dw = this.originalPosition.left + this.originalSize.width,
2744
+ dh = this.position.top + this.size.height,
2745
+ cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2746
+ if (isminw) {
2747
+ data.width = o.minWidth;
2748
+ }
2749
+ if (isminh) {
2750
+ data.height = o.minHeight;
2751
+ }
2752
+ if (ismaxw) {
2753
+ data.width = o.maxWidth;
2754
+ }
2755
+ if (ismaxh) {
2756
+ data.height = o.maxHeight;
2757
+ }
2758
 
2759
+ if (isminw && cw) {
2760
+ data.left = dw - o.minWidth;
2761
+ }
2762
+ if (ismaxw && cw) {
2763
+ data.left = dw - o.maxWidth;
2764
+ }
2765
+ if (isminh && ch) {
2766
+ data.top = dh - o.minHeight;
2767
+ }
2768
+ if (ismaxh && ch) {
2769
+ data.top = dh - o.maxHeight;
2770
+ }
2771
 
2772
  // fixing jump error on top/left - bug #2330
2773
+ if (!data.width && !data.height && !data.left && data.top) {
2774
+ data.top = null;
2775
+ } else if (!data.width && !data.height && !data.top && data.left) {
2776
+ data.left = null;
2777
+ }
2778
 
2779
  return data;
2780
  },
2781
 
2782
  _proportionallyResize: function() {
2783
 
2784
+ if (!this._proportionallyResizeElements.length) {
2785
+ return;
2786
+ }
2787
+
2788
+ var i, j, borders, paddings, prel,
2789
+ element = this.helper || this.element;
2790
 
2791
+ for ( i=0; i < this._proportionallyResizeElements.length; i++) {
2792
 
2793
+ prel = this._proportionallyResizeElements[i];
2794
 
2795
  if (!this.borderDif) {
2796
+ this.borderDif = [];
2797
+ borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
2798
+ paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
2799
 
2800
+ for ( j = 0; j < borders.length; j++ ) {
2801
+ this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
2802
+ }
 
2803
  }
2804
 
 
 
 
2805
  prel.css({
2806
  height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
2807
  width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
2808
  });
2809
 
2810
+ }
2811
 
2812
  },
2813
 
2818
 
2819
  if(this._helper) {
2820
 
2821
+ this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
 
 
 
 
2822
 
2823
  this.helper.addClass(this._helper).css({
2824
+ width: this.element.outerWidth() - 1,
2825
+ height: this.element.outerHeight() - 1,
2826
+ position: "absolute",
2827
+ left: this.elementOffset.left +"px",
2828
+ top: this.elementOffset.top +"px",
2829
  zIndex: ++o.zIndex //TODO: Don't modify option
2830
  });
2831
 
2840
  },
2841
 
2842
  _change: {
2843
+ e: function(event, dx) {
2844
  return { width: this.originalSize.width + dx };
2845
  },
2846
+ w: function(event, dx) {
2847
+ var cs = this.originalSize, sp = this.originalPosition;
2848
  return { left: sp.left + dx, width: cs.width - dx };
2849
  },
2850
  n: function(event, dx, dy) {
2851
+ var cs = this.originalSize, sp = this.originalPosition;
2852
  return { top: sp.top + dy, height: cs.height - dy };
2853
  },
2854
  s: function(event, dx, dy) {
2870
 
2871
  _propagate: function(n, event) {
2872
  $.ui.plugin.call(this, n, [event, this.ui()]);
2873
+ (n !== "resize" && this._trigger(n, event, this.ui()));
2874
  },
2875
 
2876
  plugins: {},
2889
 
2890
  });
2891
 
 
 
 
 
2892
  /*
2893
  * Resizable Extensions
2894
  */
2895
 
2896
+ $.ui.plugin.add("resizable", "animate", {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2897
 
2898
+ stop: function( event ) {
2899
+ var that = $(this).data("ui-resizable"),
2900
+ o = that.options,
2901
+ pr = that._proportionallyResizeElements,
2902
+ ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2903
+ soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
2904
+ soffsetw = ista ? 0 : that.sizeDiff.width,
2905
+ style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
2906
+ left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
2907
+ top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2908
+
2909
+ that.element.animate(
2910
+ $.extend(style, top && left ? { top: top, left: left } : {}), {
2911
+ duration: o.animateDuration,
2912
+ easing: o.animateEasing,
2913
+ step: function() {
2914
 
2915
+ var data = {
2916
+ width: parseInt(that.element.css("width"), 10),
2917
+ height: parseInt(that.element.css("height"), 10),
2918
+ top: parseInt(that.element.css("top"), 10),
2919
+ left: parseInt(that.element.css("left"), 10)
2920
+ };
2921
 
2922
+ if (pr && pr.length) {
2923
+ $(pr[0]).css({ width: data.width, height: data.height });
2924
+ }
 
2925
 
2926
+ // propagating resize, and updating values for each animation step
2927
+ that._updateCache(data);
2928
+ that._propagate("resize", event);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2929
 
2930
  }
2931
  }
2936
 
2937
  $.ui.plugin.add("resizable", "containment", {
2938
 
2939
+ start: function() {
2940
+ var element, p, co, ch, cw, width, height,
2941
+ that = $(this).data("ui-resizable"),
2942
+ o = that.options,
2943
+ el = that.element,
2944
+ oc = o.containment,
2945
+ ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
2946
+
2947
+ if (!ce) {
2948
+ return;
2949
+ }
2950
 
2951
+ that.containerElement = $(ce);
2952
 
2953
+ if (/document/.test(oc) || oc === document) {
2954
+ that.containerOffset = { left: 0, top: 0 };
2955
+ that.containerPosition = { left: 0, top: 0 };
2956
 
2957
+ that.parentData = {
2958
  element: $(document), left: 0, top: 0,
2959
  width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
2960
  };
2962
 
2963
  // i'm a node, so compute top, left, right, bottom
2964
  else {
2965
+ element = $(ce);
2966
+ p = [];
2967
  $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
2968
 
2969
+ that.containerOffset = element.offset();
2970
+ that.containerPosition = element.position();
2971
+ that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
2972
 
2973
+ co = that.containerOffset;
2974
+ ch = that.containerSize.height;
2975
+ cw = that.containerSize.width;
2976
+ width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
2977
+ height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
2978
 
2979
+ that.parentData = {
2980
  element: ce, left: co.left, top: co.top, width: width, height: height
2981
  };
2982
  }
2983
  },
2984
 
2985
+ resize: function( event ) {
2986
+ var woset, hoset, isParent, isOffsetRelative,
2987
+ that = $(this).data("ui-resizable"),
2988
+ o = that.options,
2989
+ co = that.containerOffset, cp = that.position,
2990
+ pRatio = that._aspectRatio || event.shiftKey,
2991
+ cop = { top:0, left:0 }, ce = that.containerElement;
2992
 
2993
+ if (ce[0] !== document && (/static/).test(ce.css("position"))) {
2994
+ cop = co;
2995
+ }
2996
 
2997
+ if (cp.left < (that._helper ? co.left : 0)) {
2998
+ that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
2999
+ if (pRatio) {
3000
+ that.size.height = that.size.width / that.aspectRatio;
3001
+ }
3002
+ that.position.left = o.helper ? co.left : 0;
3003
  }
3004
 
3005
+ if (cp.top < (that._helper ? co.top : 0)) {
3006
+ that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
3007
+ if (pRatio) {
3008
+ that.size.width = that.size.height * that.aspectRatio;
3009
+ }
3010
+ that.position.top = that._helper ? co.top : 0;
3011
  }
3012
 
3013
+ that.offset.left = that.parentData.left+that.position.left;
3014
+ that.offset.top = that.parentData.top+that.position.top;
3015
 
3016
+ woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
3017
+ hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
3018
 
3019
+ isParent = that.containerElement.get(0) === that.element.parent().get(0);
3020
+ isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
3021
 
3022
+ if(isParent && isOffsetRelative) {
3023
+ woset -= that.parentData.left;
3024
+ }
3025
 
3026
+ if (woset + that.size.width >= that.parentData.width) {
3027
+ that.size.width = that.parentData.width - woset;
3028
+ if (pRatio) {
3029
+ that.size.height = that.size.width / that.aspectRatio;
3030
+ }
3031
  }
3032
 
3033
+ if (hoset + that.size.height >= that.parentData.height) {
3034
+ that.size.height = that.parentData.height - hoset;
3035
+ if (pRatio) {
3036
+ that.size.width = that.size.height * that.aspectRatio;
3037
+ }
3038
  }
3039
  },
3040
 
3041
+ stop: function(){
3042
+ var that = $(this).data("ui-resizable"),
3043
+ o = that.options,
3044
+ co = that.containerOffset,
3045
+ cop = that.containerPosition,
3046
+ ce = that.containerElement,
3047
+ helper = $(that.helper),
3048
+ ho = helper.offset(),
3049
+ w = helper.outerWidth() - that.sizeDiff.width,
3050
+ h = helper.outerHeight() - that.sizeDiff.height;
3051
 
3052
+ if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
3053
  $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3054
+ }
3055
 
3056
+ if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
3057
  $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3058
+ }
3059
+
3060
+ }
3061
+ });
3062
+
3063
+ $.ui.plugin.add("resizable", "alsoResize", {
3064
+
3065
+ start: function () {
3066
+ var that = $(this).data("ui-resizable"),
3067
+ o = that.options,
3068
+ _store = function (exp) {
3069
+ $(exp).each(function() {
3070
+ var el = $(this);
3071
+ el.data("ui-resizable-alsoresize", {
3072
+ width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3073
+ left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
3074
+ });
3075
+ });
3076
+ };
3077
+
3078
+ if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
3079
+ if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3080
+ else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3081
+ }else{
3082
+ _store(o.alsoResize);
3083
+ }
3084
+ },
3085
+
3086
+ resize: function (event, ui) {
3087
+ var that = $(this).data("ui-resizable"),
3088
+ o = that.options,
3089
+ os = that.originalSize,
3090
+ op = that.originalPosition,
3091
+ delta = {
3092
+ height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
3093
+ top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
3094
+ },
3095
+
3096
+ _alsoResize = function (exp, c) {
3097
+ $(exp).each(function() {
3098
+ var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
3099
+ css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
3100
+
3101
+ $.each(css, function (i, prop) {
3102
+ var sum = (start[prop]||0) + (delta[prop]||0);
3103
+ if (sum && sum >= 0) {
3104
+ style[prop] = sum || null;
3105
+ }
3106
+ });
3107
+
3108
+ el.css(style);
3109
+ });
3110
+ };
3111
 
3112
+ if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
3113
+ $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3114
+ }else{
3115
+ _alsoResize(o.alsoResize);
3116
+ }
3117
+ },
3118
+
3119
+ stop: function () {
3120
+ $(this).removeData("resizable-alsoresize");
3121
  }
3122
  });
3123
 
3124
  $.ui.plugin.add("resizable", "ghost", {
3125
 
3126
+ start: function() {
3127
 
3128
+ var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
3129
 
3130
+ that.ghost = that.originalElement.clone();
3131
+ that.ghost
3132
+ .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
3133
+ .addClass("ui-resizable-ghost")
3134
+ .addClass(typeof o.ghost === "string" ? o.ghost : "");
3135
 
3136
+ that.ghost.appendTo(that.helper);
3137
 
3138
  },
3139
 
3140
+ resize: function(){
3141
+ var that = $(this).data("ui-resizable");
3142
+ if (that.ghost) {
3143
+ that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
3144
+ }
3145
  },
3146
 
3147
+ stop: function() {
3148
+ var that = $(this).data("ui-resizable");
3149
+ if (that.ghost && that.helper) {
3150
+ that.helper.get(0).removeChild(that.ghost.get(0));
3151
+ }
3152
  }
3153
 
3154
  });
3155
 
3156
  $.ui.plugin.add("resizable", "grid", {
3157
 
3158
+ resize: function() {
3159
+ var that = $(this).data("ui-resizable"),
3160
+ o = that.options,
3161
+ cs = that.size,
3162
+ os = that.originalSize,
3163
+ op = that.originalPosition,
3164
+ a = that.axis,
3165
+ grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
3166
+ gridX = (grid[0]||1),
3167
+ gridY = (grid[1]||1),
3168
+ ox = Math.round((cs.width - os.width) / gridX) * gridX,
3169
+ oy = Math.round((cs.height - os.height) / gridY) * gridY,
3170
+ newWidth = os.width + ox,
3171
+ newHeight = os.height + oy,
3172
+ isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
3173
+ isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
3174
+ isMinWidth = o.minWidth && (o.minWidth > newWidth),
3175
+ isMinHeight = o.minHeight && (o.minHeight > newHeight);
3176
+
3177
+ o.grid = grid;
3178
+
3179
+ if (isMinWidth) {
3180
+ newWidth = newWidth + gridX;
3181
+ }
3182
+ if (isMinHeight) {
3183
+ newHeight = newHeight + gridY;
3184
+ }
3185
+ if (isMaxWidth) {
3186
+ newWidth = newWidth - gridX;
3187
+ }
3188
+ if (isMaxHeight) {
3189
+ newHeight = newHeight - gridY;
3190
+ }
3191
 
3192
  if (/^(se|s|e)$/.test(a)) {
3193
+ that.size.width = newWidth;
3194
+ that.size.height = newHeight;
3195
+ } else if (/^(ne)$/.test(a)) {
3196
+ that.size.width = newWidth;
3197
+ that.size.height = newHeight;
3198
+ that.position.top = op.top - oy;
3199
+ } else if (/^(sw)$/.test(a)) {
3200
+ that.size.width = newWidth;
3201
+ that.size.height = newHeight;
3202
+ that.position.left = op.left - ox;
3203
+ } else {
3204
+ that.size.width = newWidth;
3205
+ that.size.height = newHeight;
3206
+ that.position.top = op.top - oy;
3207
+ that.position.left = op.left - ox;
 
 
 
3208
  }
3209
  }
3210
 
3211
  });
3212
 
 
 
 
 
 
 
 
 
3213
  })(jQuery);
3214
+
 
 
 
 
 
 
 
 
 
 
 
 
 
3215
  (function( $, undefined ) {
3216
 
3217
  $.widget("ui.selectable", $.ui.mouse, {
3218
+ version: "1.10.2",
3219
  options: {
3220
+ appendTo: "body",
3221
  autoRefresh: true,
3222
  distance: 0,
3223
+ filter: "*",
3224
+ tolerance: "touch",
3225
+
3226
+ // callbacks
3227
+ selected: null,
3228
+ selecting: null,
3229
+ start: null,
3230
+ stop: null,
3231
+ unselected: null,
3232
+ unselecting: null
3233
  },
3234
  _create: function() {
3235
+ var selectees,
3236
+ that = this;
3237
 
3238
  this.element.addClass("ui-selectable");
3239
 
3240
  this.dragged = false;
3241
 
3242
  // cache selectee children based on filter
 
3243
  this.refresh = function() {
3244
+ selectees = $(that.options.filter, that.element[0]);
3245
  selectees.addClass("ui-selectee");
3246
  selectees.each(function() {
3247
+ var $this = $(this),
3248
+ pos = $this.offset();
3249
  $.data(this, "selectable-item", {
3250
  element: this,
3251
  $element: $this,
3254
  right: pos.left + $this.outerWidth(),
3255
  bottom: pos.top + $this.outerHeight(),
3256
  startselected: false,
3257
+ selected: $this.hasClass("ui-selected"),
3258
+ selecting: $this.hasClass("ui-selecting"),
3259
+ unselecting: $this.hasClass("ui-unselecting")
3260
  });
3261
  });
3262
  };
3269
  this.helper = $("<div class='ui-selectable-helper'></div>");
3270
  },
3271
 
3272
+ _destroy: function() {
3273
  this.selectees
3274
  .removeClass("ui-selectee")
3275
  .removeData("selectable-item");
3276
  this.element
3277
+ .removeClass("ui-selectable ui-selectable-disabled");
 
 
3278
  this._mouseDestroy();
 
 
3279
  },
3280
 
3281
  _mouseStart: function(event) {
3282
+ var that = this,
3283
+ options = this.options;
3284
 
3285
  this.opos = [event.pageX, event.pageY];
3286
 
3287
+ if (this.options.disabled) {
3288
  return;
3289
+ }
 
3290
 
3291
  this.selectees = $(options.filter, this.element[0]);
3292
 
3295
  $(options.appendTo).append(this.helper);
3296
  // position helper (lasso)
3297
  this.helper.css({
3298
+ "left": event.pageX,
3299
+ "top": event.pageY,
3300
  "width": 0,
3301
  "height": 0
3302
  });
3305
  this.refresh();
3306
  }
3307
 
3308
+ this.selectees.filter(".ui-selected").each(function() {
3309
  var selectee = $.data(this, "selectable-item");
3310
  selectee.startselected = true;
3311
  if (!event.metaKey && !event.ctrlKey) {
3312
+ selectee.$element.removeClass("ui-selected");
3313
  selectee.selected = false;
3314
+ selectee.$element.addClass("ui-unselecting");
3315
  selectee.unselecting = true;
3316
  // selectable UNSELECTING callback
3317
+ that._trigger("unselecting", event, {
3318
  unselecting: selectee.element
3319
  });
3320
  }
3321
  });
3322
 
3323
+ $(event.target).parents().addBack().each(function() {
3324
+ var doSelect,
3325
+ selectee = $.data(this, "selectable-item");
3326
  if (selectee) {
3327
+ doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
3328
  selectee.$element
3329
  .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3330
  .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3333
  selectee.selected = doSelect;
3334
  // selectable (UN)SELECTING callback
3335
  if (doSelect) {
3336
+ that._trigger("selecting", event, {
3337
  selecting: selectee.element
3338
  });
3339
  } else {
3340
+ that._trigger("unselecting", event, {
3341
  unselecting: selectee.element
3342
  });
3343
  }
3348
  },
3349
 
3350
  _mouseDrag: function(event) {
3351
+
3352
  this.dragged = true;
3353
 
3354
+ if (this.options.disabled) {
3355
  return;
3356
+ }
3357
 
3358
+ var tmp,
3359
+ that = this,
3360
+ options = this.options,
3361
+ x1 = this.opos[0],
3362
+ y1 = this.opos[1],
3363
+ x2 = event.pageX,
3364
+ y2 = event.pageY;
3365
 
3366
+ if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
3367
+ if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
 
3368
  this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3369
 
3370
  this.selectees.each(function() {
3371
+ var selectee = $.data(this, "selectable-item"),
3372
+ hit = false;
3373
+
3374
  //prevent helper from being selected if appendTo: selectable
3375
+ if (!selectee || selectee.element === that.element[0]) {
3376
  return;
3377
+ }
3378
+
3379
+ if (options.tolerance === "touch") {
3380
  hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3381
+ } else if (options.tolerance === "fit") {
3382
  hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3383
  }
3384
 
3385
  if (hit) {
3386
  // SELECT
3387
  if (selectee.selected) {
3388
+ selectee.$element.removeClass("ui-selected");
3389
  selectee.selected = false;
3390
  }
3391
  if (selectee.unselecting) {
3392
+ selectee.$element.removeClass("ui-unselecting");
3393
  selectee.unselecting = false;
3394
  }
3395
  if (!selectee.selecting) {
3396
+ selectee.$element.addClass("ui-selecting");
3397
  selectee.selecting = true;
3398
  // selectable SELECTING callback
3399
+ that._trigger("selecting", event, {
3400
  selecting: selectee.element
3401
  });
3402
  }
3404
  // UNSELECT
3405
  if (selectee.selecting) {
3406
  if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3407
+ selectee.$element.removeClass("ui-selecting");
3408
  selectee.selecting = false;
3409
+ selectee.$element.addClass("ui-selected");
3410
  selectee.selected = true;
3411
  } else {
3412
+ selectee.$element.removeClass("ui-selecting");
3413
  selectee.selecting = false;
3414
  if (selectee.startselected) {
3415
+ selectee.$element.addClass("ui-unselecting");
3416
  selectee.unselecting = true;
3417
  }
3418
  // selectable UNSELECTING callback
3419
+ that._trigger("unselecting", event, {
3420
  unselecting: selectee.element
3421
  });
3422
  }
3423
  }
3424
  if (selectee.selected) {
3425
  if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3426
+ selectee.$element.removeClass("ui-selected");
3427
  selectee.selected = false;
3428
 
3429
+ selectee.$element.addClass("ui-unselecting");
3430
  selectee.unselecting = true;
3431
  // selectable UNSELECTING callback
3432
+ that._trigger("unselecting", event, {
3433
  unselecting: selectee.element
3434
  });
3435
  }
3441
  },
3442
 
3443
  _mouseStop: function(event) {
3444
+ var that = this;
3445
 
3446
  this.dragged = false;
3447
 
3448
+ $(".ui-unselecting", this.element[0]).each(function() {
 
 
3449
  var selectee = $.data(this, "selectable-item");
3450
+ selectee.$element.removeClass("ui-unselecting");
3451
  selectee.unselecting = false;
3452
  selectee.startselected = false;
3453
+ that._trigger("unselected", event, {
3454
  unselected: selectee.element
3455
  });
3456
  });
3457
+ $(".ui-selecting", this.element[0]).each(function() {
3458
  var selectee = $.data(this, "selectable-item");
3459
+ selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
3460
  selectee.selecting = false;
3461
  selectee.selected = true;
3462
  selectee.startselected = true;
3463
+ that._trigger("selected", event, {
3464
  selected: selectee.element
3465
  });
3466
  });
3473
 
3474
  });
3475
 
 
 
 
 
3476
  })(jQuery);
3477
+
 
 
 
 
 
 
 
 
 
 
 
 
 
3478
  (function( $, undefined ) {
3479
 
3480
+ /*jshint loopfunc: true */
3481
+
3482
+ function isOverAxis( x, reference, size ) {
3483
+ return ( x > reference ) && ( x < ( reference + size ) );
3484
+ }
3485
+
3486
+ function isFloating(item) {
3487
+ return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
3488
+ }
3489
+
3490
  $.widget("ui.sortable", $.ui.mouse, {
3491
+ version: "1.10.2",
3492
  widgetEventPrefix: "sort",
3493
+ ready: false,
3494
  options: {
3495
  appendTo: "parent",
3496
  axis: false,
3497
  connectWith: false,
3498
  containment: false,
3499
+ cursor: "auto",
3500
  cursorAt: false,
3501
  dropOnEmpty: true,
3502
  forcePlaceholderSize: false,
3504
  grid: false,
3505
  handle: false,
3506
  helper: "original",
3507
+ items: "> *",
3508
  opacity: false,
3509
  placeholder: false,
3510
  revert: false,
3513
  scrollSpeed: 20,
3514
  scope: "default",
3515
  tolerance: "intersect",
3516
+ zIndex: 1000,
3517
+
3518
+ // callbacks
3519
+ activate: null,
3520
+ beforeStop: null,
3521
+ change: null,
3522
+ deactivate: null,
3523
+ out: null,
3524
+ over: null,
3525
+ receive: null,
3526
+ remove: null,
3527
+ sort: null,
3528
+ start: null,
3529
+ stop: null,
3530
+ update: null
3531
  },
3532
  _create: function() {
3533
 
3539
  this.refresh();
3540
 
3541
  //Let's determine if the items are being displayed horizontally
3542
+ this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
3543
 
3544
  //Let's determine the parent's offset
3545
  this.offset = this.element.offset();
3547
  //Initialize mouse events for interaction
3548
  this._mouseInit();
3549
 
3550
+ //We're ready to go
3551
+ this.ready = true;
3552
+
3553
  },
3554
 
3555
+ _destroy: function() {
3556
  this.element
3557
  .removeClass("ui-sortable ui-sortable-disabled");
3558
  this._mouseDestroy();
3559
 
3560
+ for ( var i = this.items.length - 1; i >= 0; i-- ) {
3561
  this.items[i].item.removeData(this.widgetName + "-item");
3562
+ }
3563
 
3564
  return this;
3565
  },
3567
  _setOption: function(key, value){
3568
  if ( key === "disabled" ) {
3569
  this.options[ key ] = value;
3570
+
3571
+ this.widget().toggleClass( "ui-sortable-disabled", !!value );
 
3572
  } else {
3573
  // Don't call widget base _setOption for disable as it adds ui-state-disabled class
3574
  $.Widget.prototype._setOption.apply(this, arguments);
3576
  },
3577
 
3578
  _mouseCapture: function(event, overrideHandle) {
3579
+ var currentItem = null,
3580
+ validHandle = false,
3581
+ that = this;
3582
 
3583
  if (this.reverting) {
3584
  return false;
3585
  }
3586
 
3587
+ if(this.options.disabled || this.options.type === "static") {
3588
+ return false;
3589
+ }
3590
 
3591
  //We have to refresh the items data once first
3592
  this._refreshItems(event);
3593
 
3594
  //Find out if the clicked node (or one of its parents) is a actual item in this.items
3595
+ $(event.target).parents().each(function() {
3596
+ if($.data(this, that.widgetName + "-item") === that) {
3597
  currentItem = $(this);
3598
  return false;
3599
  }
3600
  });
3601
+ if($.data(event.target, that.widgetName + "-item") === that) {
3602
+ currentItem = $(event.target);
3603
+ }
3604
 
3605
+ if(!currentItem) {
3606
+ return false;
3607
+ }
3608
  if(this.options.handle && !overrideHandle) {
3609
+ $(this.options.handle, currentItem).find("*").addBack().each(function() {
3610
+ if(this === event.target) {
3611
+ validHandle = true;
3612
+ }
3613
+ });
3614
+ if(!validHandle) {
3615
+ return false;
3616
+ }
3617
  }
3618
 
3619
  this.currentItem = currentItem;
3624
 
3625
  _mouseStart: function(event, overrideHandle, noActivation) {
3626
 
3627
+ var i, body,
3628
+ o = this.options;
3629
+
3630
  this.currentContainer = this;
3631
 
3632
  //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
3656
  left: this.offset.left - this.margins.left
3657
  };
3658
 
 
 
 
 
 
3659
  $.extend(this.offset, {
3660
  click: { //Where the click happened, relative to the element
3661
  left: event.pageX - this.offset.left,
3665
  relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
3666
  });
3667
 
3668
+ // Only after we got the offset, we can change the helper's position to absolute
3669
+ // TODO: Still need to figure out a way to make relative sorting possible
3670
+ this.helper.css("position", "absolute");
3671
+ this.cssPosition = this.helper.css("position");
3672
+
3673
  //Generate the original position
3674
  this.originalPosition = this._generatePosition(event);
3675
  this.originalPageX = event.pageX;
3676
  this.originalPageY = event.pageY;
3677
 
3678
+ //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
3679
  (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
3680
 
3681
  //Cache the former DOM position
3682
  this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
3683
 
3684
  //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
3685
+ if(this.helper[0] !== this.currentItem[0]) {
3686
  this.currentItem.hide();
3687
  }
3688
 
3690
  this._createPlaceholder();
3691
 
3692
  //Set a containment if given in the options
3693
+ if(o.containment) {
3694
  this._setContainment();
3695
+ }
3696
+
3697
+ if( o.cursor && o.cursor !== "auto" ) { // cursor option
3698
+ body = this.document.find( "body" );
3699
 
3700
+ // support: IE
3701
+ this.storedCursor = body.css( "cursor" );
3702
+ body.css( "cursor", o.cursor );
3703
+
3704
+ this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
3705
  }
3706
 
3707
  if(o.opacity) { // opacity option
3708
+ if (this.helper.css("opacity")) {
3709
+ this._storedOpacity = this.helper.css("opacity");
3710
+ }
3711
  this.helper.css("opacity", o.opacity);
3712
  }
3713
 
3714
  if(o.zIndex) { // zIndex option
3715
+ if (this.helper.css("zIndex")) {
3716
+ this._storedZIndex = this.helper.css("zIndex");
3717
+ }
3718
  this.helper.css("zIndex", o.zIndex);
3719
  }
3720
 
3721
  //Prepare scrolling
3722
+ if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
3723
  this.overflowOffset = this.scrollParent.offset();
3724
+ }
3725
 
3726
  //Call callbacks
3727
  this._trigger("start", event, this._uiHash());
3728
 
3729
  //Recache the helper size
3730
+ if(!this._preserveHelperProportions) {
3731
  this._cacheHelperProportions();
3732
+ }
3733
 
3734
 
3735
+ //Post "activate" events to possible containers
3736
+ if( !noActivation ) {
3737
+ for ( i = this.containers.length - 1; i >= 0; i-- ) {
3738
+ this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
3739
+ }
3740
  }
3741
 
3742
  //Prepare possible droppables
3743
+ if($.ui.ddmanager) {
3744
  $.ui.ddmanager.current = this;
3745
+ }
3746
 
3747
+ if ($.ui.ddmanager && !o.dropBehaviour) {
3748
  $.ui.ddmanager.prepareOffsets(this, event);
3749
+ }
3750
 
3751
  this.dragging = true;
3752
 
3757
  },
3758
 
3759
  _mouseDrag: function(event) {
3760
+ var i, item, itemElement, intersection,
3761
+ o = this.options,
3762
+ scrolled = false;
3763
 
3764
  //Compute the helpers position
3765
  this.position = this._generatePosition(event);
3771
 
3772
  //Do scrolling
3773
  if(this.options.scroll) {
3774
+ if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
 
3775
 
3776
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
3777
  this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
3778
+ } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
3779
  this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
3780
+ }
3781
 
3782
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
3783
  this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
3784
+ } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
3785
  this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
3786
+ }
3787
 
3788
  } else {
3789
 
3790
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
3791
  scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
3792
+ } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
3793
  scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
3794
+ }
3795
 
3796
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
3797
  scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
3798
+ } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
3799
  scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
3800
+ }
3801
 
3802
  }
3803
 
3804
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
3805
  $.ui.ddmanager.prepareOffsets(this, event);
3806
+ }
3807
  }
3808
 
3809
  //Regenerate the absolute position used for position checks
3810
  this.positionAbs = this._convertPositionTo("absolute");
3811
 
3812
  //Set the helper position
3813
+ if(!this.options.axis || this.options.axis !== "y") {
3814
+ this.helper[0].style.left = this.position.left+"px";
3815
+ }
3816
+ if(!this.options.axis || this.options.axis !== "x") {
3817
+ this.helper[0].style.top = this.position.top+"px";
3818
+ }
3819
 
3820
  //Rearrange
3821
+ for (i = this.items.length - 1; i >= 0; i--) {
3822
 
3823
  //Cache variables and intersection, continue if no intersection
3824
+ item = this.items[i];
3825
+ itemElement = item.item[0];
3826
+ intersection = this._intersectsWithPointer(item);
3827
+ if (!intersection) {
3828
+ continue;
3829
+ }
3830
+
3831
+ // Only put the placeholder inside the current Container, skip all
3832
+ // items form other containers. This works because when moving
3833
+ // an item from one container to another the
3834
+ // currentContainer is switched before the placeholder is moved.
3835
+ //
3836
+ // Without this moving items in "sub-sortables" can cause the placeholder to jitter
3837
+ // beetween the outer and inner container.
3838
+ if (item.instance !== this.currentContainer) {
3839
+ continue;
3840
+ }
3841
+
3842
+ // cannot intersect with itself
3843
+ // no useless actions that have been done before
3844
+ // no action if the item moved is the parent of the item checked
3845
+ if (itemElement !== this.currentItem[0] &&
3846
+ this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
3847
+ !$.contains(this.placeholder[0], itemElement) &&
3848
+ (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
3849
  ) {
3850
 
3851
+ this.direction = intersection === 1 ? "down" : "up";
3852
 
3853
+ if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
3854
  this._rearrange(event, item);
3855
  } else {
3856
  break;
3865
  this._contactContainers(event);
3866
 
3867
  //Interconnect with droppables
3868
+ if($.ui.ddmanager) {
3869
+ $.ui.ddmanager.drag(this, event);
3870
+ }
3871
 
3872
  //Call callbacks
3873
+ this._trigger("sort", event, this._uiHash());
3874
 
3875
  this.lastPositionAbs = this.positionAbs;
3876
  return false;
3879
 
3880
  _mouseStop: function(event, noPropagation) {
3881
 
3882
+ if(!event) {
3883
+ return;
3884
+ }
3885
 
3886
  //If we are using droppables, inform the manager about the drop
3887
+ if ($.ui.ddmanager && !this.options.dropBehaviour) {
3888
  $.ui.ddmanager.drop(this, event);
3889
+ }
3890
 
3891
  if(this.options.revert) {
3892
+ var that = this,
3893
+ cur = this.placeholder.offset(),
3894
+ axis = this.options.axis,
3895
+ animation = {};
3896
 
3897
+ if ( !axis || axis === "x" ) {
3898
+ animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
3899
+ }
3900
+ if ( !axis || axis === "y" ) {
3901
+ animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
3902
+ }
3903
+ this.reverting = true;
3904
+ $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
3905
+ that._clear(event);
3906
  });
3907
  } else {
3908
  this._clear(event, noPropagation);
3914
 
3915
  cancel: function() {
3916
 
 
 
3917
  if(this.dragging) {
3918
 
3919
  this._mouseUp({ target: null });
3920
 
3921
+ if(this.options.helper === "original") {
3922
  this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3923
+ } else {
3924
  this.currentItem.show();
3925
+ }
3926
 
3927
  //Post deactivating events to containers
3928
  for (var i = this.containers.length - 1; i >= 0; i--){
3929
+ this.containers[i]._trigger("deactivate", null, this._uiHash(this));
3930
  if(this.containers[i].containerCache.over) {
3931
+ this.containers[i]._trigger("out", null, this._uiHash(this));
3932
  this.containers[i].containerCache.over = 0;
3933
  }
3934
  }
3937
 
3938
  if (this.placeholder) {
3939
  //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3940
+ if(this.placeholder[0].parentNode) {
3941
+ this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3942
+ }
3943
+ if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
3944
+ this.helper.remove();
3945
+ }
3946
 
3947
  $.extend(this, {
3948
  helper: null,
3964
 
3965
  serialize: function(o) {
3966
 
3967
+ var items = this._getItemsAsjQuery(o && o.connected),
3968
+ str = [];
3969
+ o = o || {};
3970
 
3971
  $(items).each(function() {
3972
+ var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
3973
+ if (res) {
3974
+ str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
3975
+ }
3976
  });
3977
 
3978
  if(!str.length && o.key) {
3979
+ str.push(o.key + "=");
3980
  }
3981
 
3982
+ return str.join("&");
3983
 
3984
  },
3985
 
3986
  toArray: function(o) {
3987
 
3988
+ var items = this._getItemsAsjQuery(o && o.connected),
3989
+ ret = [];
3990
+
3991
+ o = o || {};
3992
 
3993
+ items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
3994
  return ret;
3995
 
3996
  },
4001
  var x1 = this.positionAbs.left,
4002
  x2 = x1 + this.helperProportions.width,
4003
  y1 = this.positionAbs.top,
4004
+ y2 = y1 + this.helperProportions.height,
4005
+ l = item.left,
 
4006
  r = l + item.width,
4007
  t = item.top,
4008
+ b = t + item.height,
4009
+ dyClick = this.offset.click.top,
4010
+ dxClick = this.offset.click.left,
4011
+ isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
4012
+
4013
+ if ( this.options.tolerance === "pointer" ||
4014
+ this.options.forcePointerForContainers ||
4015
+ (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
 
 
4016
  ) {
4017
  return isOverElement;
4018
  } else {
4019
 
4020
+ return (l < x1 + (this.helperProportions.width / 2) && // Right Half
4021
+ x2 - (this.helperProportions.width / 2) < r && // Left Half
4022
+ t < y1 + (this.helperProportions.height / 2) && // Bottom Half
4023
+ y2 - (this.helperProportions.height / 2) < b ); // Top Half
4024
 
4025
  }
4026
  },
4027
 
4028
  _intersectsWithPointer: function(item) {
4029
 
4030
+ var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4031
+ isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4032
  isOverElement = isOverElementHeight && isOverElementWidth,
4033
  verticalDirection = this._getDragVerticalDirection(),
4034
  horizontalDirection = this._getDragHorizontalDirection();
4035
 
4036
+ if (!isOverElement) {
4037
  return false;
4038
+ }
4039
 
4040
  return this.floating ?
4041
+ ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
4042
+ : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
4043
 
4044
  },
4045
 
4046
  _intersectsWithSides: function(item) {
4047
 
4048
+ var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4049
+ isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4050
  verticalDirection = this._getDragVerticalDirection(),
4051
  horizontalDirection = this._getDragHorizontalDirection();
4052
 
4053
  if (this.floating && horizontalDirection) {
4054
+ return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
4055
  } else {
4056
+ return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
4057
  }
4058
 
4059
  },
4060
 
4061
  _getDragVerticalDirection: function() {
4062
  var delta = this.positionAbs.top - this.lastPositionAbs.top;
4063
+ return delta !== 0 && (delta > 0 ? "down" : "up");
4064
  },
4065
 
4066
  _getDragHorizontalDirection: function() {
4067
  var delta = this.positionAbs.left - this.lastPositionAbs.left;
4068
+ return delta !== 0 && (delta > 0 ? "right" : "left");
4069
  },
4070
 
4071
  refresh: function(event) {
4076
 
4077
  _connectWith: function() {
4078
  var options = this.options;
4079
+ return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
 
 
4080
  },
4081
+
4082
  _getItemsAsjQuery: function(connected) {
4083
 
4084
+ var i, j, cur, inst,
4085
+ items = [],
4086
+ queries = [],
4087
+ connectWith = this._connectWith();
4088
 
4089
  if(connectWith && connected) {
4090
+ for (i = connectWith.length - 1; i >= 0; i--){
4091
+ cur = $(connectWith[i]);
4092
+ for ( j = cur.length - 1; j >= 0; j--){
4093
+ inst = $.data(cur[j], this.widgetFullName);
4094
+ if(inst && inst !== this && !inst.options.disabled) {
4095
+ 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]);
4096
  }
4097
+ }
4098
+ }
4099
  }
4100
 
4101
+ 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]);
4102
 
4103
+ for (i = queries.length - 1; i >= 0; i--){
4104
  queries[i][0].each(function() {
4105
  items.push(this);
4106
  });
4107
+ }
4108
 
4109
  return $(items);
4110
 
4114
 
4115
  var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4116
 
4117
+ this.items = $.grep(this.items, function (item) {
 
4118
  for (var j=0; j < list.length; j++) {
4119
+ if(list[j] === item.item[0]) {
4120
+ return false;
4121
+ }
4122
+ }
4123
+ return true;
4124
+ });
4125
 
4126
  },
4127
 
4129
 
4130
  this.items = [];
4131
  this.containers = [this];
4132
+
4133
+ var i, j, cur, inst, targetData, _queries, item, queriesLength,
4134
+ items = this.items,
4135
+ queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
4136
+ connectWith = this._connectWith();
4137
+
4138
+ if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4139
+ for (i = connectWith.length - 1; i >= 0; i--){
4140
+ cur = $(connectWith[i]);
4141
+ for (j = cur.length - 1; j >= 0; j--){
4142
+ inst = $.data(cur[j], this.widgetFullName);
4143
+ if(inst && inst !== this && !inst.options.disabled) {
4144
  queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4145
  this.containers.push(inst);
4146
  }
4147
+ }
4148
+ }
4149
  }
4150
 
4151
+ for (i = queries.length - 1; i >= 0; i--) {
4152
+ targetData = queries[i][1];
4153
+ _queries = queries[i][0];
4154
 
4155
+ for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4156
+ item = $(_queries[j]);
4157
 
4158
+ item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
4159
 
4160
  items.push({
4161
  item: item,
4163
  width: 0, height: 0,
4164
  left: 0, top: 0
4165
  });
4166
+ }
4167
+ }
4168
 
4169
  },
4170
 
4175
  this.offset.parent = this._getParentOffset();
4176
  }
4177
 
4178
+ var i, item, t, p;
4179
+
4180
+ for (i = this.items.length - 1; i >= 0; i--){
4181
+ item = this.items[i];
4182
 
4183
  //We ignore calculating positions of all connected containers when we're not over them
4184
+ if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
4185
  continue;
4186
+ }
4187
 
4188
+ t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4189
 
4190
  if (!fast) {
4191
  item.width = t.outerWidth();
4192
  item.height = t.outerHeight();
4193
  }
4194
 
4195
+ p = t.offset();
4196
  item.left = p.left;
4197
  item.top = p.top;
4198
+ }
4199
 
4200
  if(this.options.custom && this.options.custom.refreshContainers) {
4201
  this.options.custom.refreshContainers.call(this);
4202
  } else {
4203
+ for (i = this.containers.length - 1; i >= 0; i--){
4204
+ p = this.containers[i].element.offset();
4205
  this.containers[i].containerCache.left = p.left;
4206
  this.containers[i].containerCache.top = p.top;
4207
  this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4208
  this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4209
+ }
4210
  }
4211
 
4212
  return this;
4213
  },
4214
 
4215
  _createPlaceholder: function(that) {
4216
+ that = that || this;
4217
+ var className,
4218
+ o = that.options;
4219
 
4220
+ if(!o.placeholder || o.placeholder.constructor === String) {
4221
+ className = o.placeholder;
 
 
4222
  o.placeholder = {
4223
  element: function() {
4224
 
4225
+ var nodeName = that.currentItem[0].nodeName.toLowerCase(),
4226
+ element = $( that.document[0].createElement( nodeName ) )
4227
+ .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
4228
+ .removeClass("ui-sortable-helper");
4229
+
4230
+ if ( nodeName === "tr" ) {
4231
+ // Use a high colspan to force the td to expand the full
4232
+ // width of the table (browsers are smart enough to
4233
+ // handle this properly)
4234
+ element.append( "<td colspan='99'>&#160;</td>" );
4235
+ } else if ( nodeName === "img" ) {
4236
+ element.attr( "src", that.currentItem.attr( "src" ) );
4237
+ }
4238
 
4239
+ if ( !className ) {
4240
+ element.css( "visibility", "hidden" );
4241
+ }
4242
 
4243
+ return element;
4244
  },
4245
  update: function(container, p) {
4246
 
4247
  // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4248
  // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4249
+ if(className && !o.forcePlaceholderSize) {
4250
+ return;
4251
+ }
4252
 
4253
  //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
4254
+ if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
4255
+ if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
4256
  }
4257
  };
4258
  }
4259
 
4260
  //Create the placeholder
4261
+ that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4262
 
4263
  //Append it after the actual current item
4264
+ that.currentItem.after(that.placeholder);
4265
 
4266
  //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4267
+ o.placeholder.update(that, that.placeholder);
4268
 
4269
  },
4270
 
4271
  _contactContainers: function(event) {
4272
+ var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
4273
+ innermostContainer = null,
4274
+ innermostIndex = null;
 
 
 
 
 
 
 
4275
 
4276
+ // get innermost container that intersects with item
4277
+ for (i = this.containers.length - 1; i >= 0; i--) {
4278
 
4279
+ // never consider a container that's located within the item itself
4280
+ if($.contains(this.currentItem[0], this.containers[i].element[0])) {
4281
+ continue;
4282
+ }
4283
+
4284
+ if(this._intersectsWith(this.containers[i].containerCache)) {
4285
+
4286
+ // if we've already found a container and it's more "inner" than this, then continue
4287
+ if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
4288
  continue;
4289
+ }
4290
 
4291
+ innermostContainer = this.containers[i];
4292
  innermostIndex = i;
4293
+
4294
  } else {
4295
+ // container doesn't intersect. trigger "out" event if necessary
4296
  if(this.containers[i].containerCache.over) {
4297
  this.containers[i]._trigger("out", event, this._uiHash(this));
4298
  this.containers[i].containerCache.over = 0;
4300
  }
4301
 
4302
  }
4303
+
4304
+ // if no intersecting containers found, return
4305
+ if(!innermostContainer) {
4306
+ return;
4307
+ }
4308
 
4309
  // move the item into the container if it's not there already
4310
  if(this.containers.length === 1) {
4311
+ if (!this.containers[innermostIndex].containerCache.over) {
4312
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4313
+ this.containers[innermostIndex].containerCache.over = 1;
4314
+ }
4315
+ } else {
4316
+
4317
+ //When entering a new container, we will find the item with the least distance and append our item near it
4318
+ dist = 10000;
4319
+ itemWithLeastDistance = null;
4320
+ floating = innermostContainer.floating || isFloating(this.currentItem);
4321
+ posProperty = floating ? "left" : "top";
4322
+ sizeProperty = floating ? "width" : "height";
4323
+ base = this.positionAbs[posProperty] + this.offset.click[posProperty];
4324
+ for (j = this.items.length - 1; j >= 0; j--) {
4325
+ if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
4326
+ continue;
4327
+ }
4328
+ if(this.items[j].item[0] === this.currentItem[0]) {
4329
+ continue;
4330
+ }
4331
+ if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
4332
+ continue;
4333
+ }
4334
+ cur = this.items[j].item.offset()[posProperty];
4335
+ nearBottom = false;
4336
+ if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
4337
+ nearBottom = true;
4338
+ cur += this.items[j][sizeProperty];
4339
+ }
4340
+
4341
+ if(Math.abs(cur - base) < dist) {
4342
+ dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4343
+ this.direction = nearBottom ? "up": "down";
4344
+ }
4345
+ }
4346
+
4347
+ //Check if dropOnEmpty is enabled
4348
+ if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
4349
+ return;
4350
+ }
4351
+
4352
+ if(this.currentContainer === this.containers[innermostIndex]) {
4353
+ return;
4354
+ }
4355
+
4356
+ itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4357
+ this._trigger("change", event, this._uiHash());
4358
+ this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4359
+ this.currentContainer = this.containers[innermostIndex];
4360
+
4361
+ //Update the placeholder
4362
+ this.options.placeholder.update(this.currentContainer, this.placeholder);
4363
+
4364
  this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4365
  this.containers[innermostIndex].containerCache.over = 1;
4366
+ }
4367
+
4368
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4369
  },
4370
 
4371
  _createHelper: function(event) {
4372
 
4373
+ var o = this.options,
4374
+ helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
4375
 
4376
+ //Add the helper to the DOM if that didn't happen already
4377
+ if(!helper.parents("body").length) {
4378
+ $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4379
+ }
4380
 
4381
+ if(helper[0] === this.currentItem[0]) {
4382
  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") };
4383
+ }
4384
 
4385
+ if(!helper[0].style.width || o.forceHelperSize) {
4386
+ helper.width(this.currentItem.width());
4387
+ }
4388
+ if(!helper[0].style.height || o.forceHelperSize) {
4389
+ helper.height(this.currentItem.height());
4390
+ }
4391
 
4392
  return helper;
4393
 
4394
  },
4395
 
4396
  _adjustOffsetFromHelper: function(obj) {
4397
+ if (typeof obj === "string") {
4398
+ obj = obj.split(" ");
4399
  }
4400
  if ($.isArray(obj)) {
4401
  obj = {left: +obj[0], top: +obj[1] || 0};
4402
  }
4403
+ if ("left" in obj) {
4404
  this.offset.click.left = obj.left + this.margins.left;
4405
  }
4406
+ if ("right" in obj) {
4407
  this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4408
  }
4409
+ if ("top" in obj) {
4410
  this.offset.click.top = obj.top + this.margins.top;
4411
  }
4412
+ if ("bottom" in obj) {
4413
  this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4414
  }
4415
  },
4425
  // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4426
  // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4427
  // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4428
+ if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
4429
  po.left += this.scrollParent.scrollLeft();
4430
  po.top += this.scrollParent.scrollTop();
4431
  }
4432
 
4433
+ // This needs to be actually done for all browsers, since pageX/pageY includes this information
4434
+ // with an ugly IE fix
4435
+ if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
4436
  po = { top: 0, left: 0 };
4437
+ }
4438
 
4439
  return {
4440
  top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4445
 
4446
  _getRelativeOffset: function() {
4447
 
4448
+ if(this.cssPosition === "relative") {
4449
  var p = this.currentItem.position();
4450
  return {
4451
  top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4473
 
4474
  _setContainment: function() {
4475
 
4476
+ var ce, co, over,
4477
+ o = this.options;
4478
+ if(o.containment === "parent") {
4479
+ o.containment = this.helper[0].parentNode;
4480
+ }
4481
+ if(o.containment === "document" || o.containment === "window") {
4482
+ this.containment = [
4483
+ 0 - this.offset.relative.left - this.offset.parent.left,
4484
+ 0 - this.offset.relative.top - this.offset.parent.top,
4485
+ $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
4486
+ ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4487
+ ];
4488
+ }
4489
 
4490
  if(!(/^(document|window|parent)$/).test(o.containment)) {
4491
+ ce = $(o.containment)[0];
4492
+ co = $(o.containment).offset();
4493
+ over = ($(ce).css("overflow") !== "hidden");
4494
 
4495
  this.containment = [
4496
  co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
4504
 
4505
  _convertPositionTo: function(d, pos) {
4506
 
4507
+ if(!pos) {
4508
+ pos = this.position;
4509
+ }
4510
+ var mod = d === "absolute" ? 1 : -1,
4511
+ scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
4512
+ scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4513
 
4514
  return {
4515
  top: (
4516
+ pos.top + // The absolute mouse position
4517
+ this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4518
+ this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
4519
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
4520
  ),
4521
  left: (
4522
+ pos.left + // The absolute mouse position
4523
+ this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4524
+ this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
4525
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
4526
  )
4527
  };
4528
 
4530
 
4531
  _generatePosition: function(event) {
4532
 
4533
+ var top, left,
4534
+ o = this.options,
4535
+ pageX = event.pageX,
4536
+ pageY = event.pageY,
4537
+ scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4538
 
4539
  // This is another very weird special case that only happens for relative elements:
4540
  // 1. If the css position is relative
4541
  // 2. and the scroll parent is the document or similar to the offset parent
4542
  // we have to refresh the relative offset during the scroll so there are no jumps
4543
+ if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
4544
  this.offset.relative = this._getRelativeOffset();
4545
  }
4546
 
 
 
 
4547
  /*
4548
  * - Position constraining -
4549
  * Constrain the position to a mix of grid, containment.
4552
  if(this.originalPosition) { //If we are not dragging yet, we won't check for options
4553
 
4554
  if(this.containment) {
4555
+ if(event.pageX - this.offset.click.left < this.containment[0]) {
4556
+ pageX = this.containment[0] + this.offset.click.left;
4557
+ }
4558
+ if(event.pageY - this.offset.click.top < this.containment[1]) {
4559
+ pageY = this.containment[1] + this.offset.click.top;
4560
+ }
4561
+ if(event.pageX - this.offset.click.left > this.containment[2]) {
4562
+ pageX = this.containment[2] + this.offset.click.left;
4563
+ }
4564
+ if(event.pageY - this.offset.click.top > this.containment[3]) {
4565
+ pageY = this.containment[3] + this.offset.click.top;
4566
+ }
4567
  }
4568
 
4569
  if(o.grid) {
4570
+ top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
4571
+ 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;
4572
 
4573
+ left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
4574
+ 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;
4575
  }
4576
 
4577
  }
4578
 
4579
  return {
4580
  top: (
4581
+ pageY - // The absolute mouse position
4582
+ this.offset.click.top - // Click offset (relative to the element)
4583
+ this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
4584
+ this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
4585
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
4586
  ),
4587
  left: (
4588
+ pageX - // The absolute mouse position
4589
+ this.offset.click.left - // Click offset (relative to the element)
4590
+ this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
4591
+ this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
4592
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
4593
  )
4594
  };
4595
 
4597
 
4598
  _rearrange: function(event, i, a, hardRefresh) {
4599
 
4600
+ 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));
4601
 
4602
  //Various things done here to improve the performance:
4603
  // 1. we create a setTimeout, that calls refreshPositions
4605
  // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
4606
  // 4. this lets only the last addition to the timeout stack through
4607
  this.counter = this.counter ? ++this.counter : 1;
4608
+ var counter = this.counter;
4609
 
4610
+ this._delay(function() {
4611
+ if(counter === this.counter) {
4612
+ this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
4613
+ }
4614
+ });
4615
 
4616
  },
4617
 
4620
  this.reverting = false;
4621
  // We delay all events that have to be triggered to after the point where the placeholder has been removed and
4622
  // everything else normalized again
4623
+ var i,
4624
+ delayedTriggers = [];
4625
 
4626
  // We first have to update the dom position of the actual currentItem
4627
  // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
4628
+ if(!this._noFinalSort && this.currentItem.parent().length) {
4629
+ this.placeholder.before(this.currentItem);
4630
+ }
4631
  this._noFinalSort = null;
4632
 
4633
+ if(this.helper[0] === this.currentItem[0]) {
4634
+ for(i in this._storedCSS) {
4635
+ if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
4636
+ this._storedCSS[i] = "";
4637
+ }
4638
  }
4639
  this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4640
  } else {
4641
  this.currentItem.show();
4642
  }
4643
 
4644
+ if(this.fromOutside && !noPropagation) {
4645
+ delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
4646
+ }
4647
+ if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
4648
+ delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
4649
+ }
4650
+
4651
+ // Check if the items Container has Changed and trigger appropriate
4652
+ // events.
4653
+ if (this !== this.currentContainer) {
4654
+ if(!noPropagation) {
4655
+ delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
4656
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4657
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4658
+ }
4659
+ }
4660
+
4661
 
4662
  //Post events to containers
4663
+ for (i = this.containers.length - 1; i >= 0; i--){
4664
+ if(!noPropagation) {
4665
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4666
+ }
4667
  if(this.containers[i].containerCache.over) {
4668
  delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4669
  this.containers[i].containerCache.over = 0;
4671
  }
4672
 
4673
  //Do what was originally in plugins
4674
+ if ( this.storedCursor ) {
4675
+ this.document.find( "body" ).css( "cursor", this.storedCursor );
4676
+ this.storedStylesheet.remove();
4677
+ }
4678
+ if(this._storedOpacity) {
4679
+ this.helper.css("opacity", this._storedOpacity);
4680
+ }
4681
+ if(this._storedZIndex) {
4682
+ this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
4683
+ }
4684
 
4685
  this.dragging = false;
4686
  if(this.cancelHelperRemoval) {
4687
  if(!noPropagation) {
4688
  this._trigger("beforeStop", event, this._uiHash());
4689
+ for (i=0; i < delayedTriggers.length; i++) {
4690
+ delayedTriggers[i].call(this, event);
4691
+ } //Trigger all delayed events
4692
  this._trigger("stop", event, this._uiHash());
4693
  }
4694
+
4695
+ this.fromOutside = false;
4696
  return false;
4697
  }
4698
 
4699
+ if(!noPropagation) {
4700
+ this._trigger("beforeStop", event, this._uiHash());
4701
+ }
4702
 
4703
  //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4704
  this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4705
 
4706
+ if(this.helper[0] !== this.currentItem[0]) {
4707
+ this.helper.remove();
4708
+ }
4709
+ this.helper = null;
4710
 
4711
  if(!noPropagation) {
4712
+ for (i=0; i < delayedTriggers.length; i++) {
4713
+ delayedTriggers[i].call(this, event);
4714
+ } //Trigger all delayed events
4715
  this._trigger("stop", event, this._uiHash());
4716
  }
4717
 
4726
  }
4727
  },
4728
 
4729
+ _uiHash: function(_inst) {
4730
+ var inst = _inst || this;
4731
  return {
4732
+ helper: inst.helper,
4733
+ placeholder: inst.placeholder || $([]),
4734
+ position: inst.position,
4735
+ originalPosition: inst.originalPosition,
4736
+ offset: inst.positionAbs,
4737
+ item: inst.currentItem,
4738
+ sender: _inst ? _inst.element : null
4739
  };
4740
  }
4741
 
4742
  });
4743
 
 
 
 
 
4744
  })(jQuery);
4745
+
4746
+ (function($, undefined) {
4747
+
4748
+ var dataSpace = "ui-effects-";
4749
+
4750
+ $.effects = {
4751
+ effect: {}
4752
+ };
4753
+
4754
+ /*!
4755
+ * jQuery Color Animations v2.1.2
4756
+ * https://github.com/jquery/jquery-color
4757
  *
4758
+ * Copyright 2013 jQuery Foundation and other contributors
4759
+ * Released under the MIT license.
4760
  * http://jquery.org/license
4761
  *
4762
+ * Date: Wed Jan 16 08:47:09 2013 -0600
 
 
 
 
4763
  */
4764
+ (function( jQuery, undefined ) {
4765
+
4766
+ var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
4767
+
4768
+ // plusequals test for += 100 -= 100
4769
+ rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
4770
+ // a set of RE's that can match strings and generate color tuples.
4771
+ stringParsers = [{
4772
+ re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4773
+ parse: function( execResult ) {
4774
+ return [
4775
+ execResult[ 1 ],
4776
+ execResult[ 2 ],
4777
+ execResult[ 3 ],
4778
+ execResult[ 4 ]
4779
+ ];
4780
+ }
4781
+ }, {
4782
+ re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4783
+ parse: function( execResult ) {
4784
+ return [
4785
+ execResult[ 1 ] * 2.55,
4786
+ execResult[ 2 ] * 2.55,
4787
+ execResult[ 3 ] * 2.55,
4788
+ execResult[ 4 ]
4789
+ ];
4790
+ }
4791
+ }, {
4792
+ // this regex ignores A-F because it's compared against an already lowercased string
4793
+ re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
4794
+ parse: function( execResult ) {
4795
+ return [
4796
+ parseInt( execResult[ 1 ], 16 ),
4797
+ parseInt( execResult[ 2 ], 16 ),
4798
+ parseInt( execResult[ 3 ], 16 )
4799
+ ];
4800
+ }
4801
+ }, {
4802
+ // this regex ignores A-F because it's compared against an already lowercased string
4803
+ re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
4804
+ parse: function( execResult ) {
4805
+ return [
4806
+ parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
4807
+ parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
4808
+ parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
4809
+ ];
4810
+ }
4811
+ }, {
4812
+ re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4813
+ space: "hsla",
4814
+ parse: function( execResult ) {
4815
+ return [
4816
+ execResult[ 1 ],
4817
+ execResult[ 2 ] / 100,
4818
+ execResult[ 3 ] / 100,
4819
+ execResult[ 4 ]
4820
+ ];
4821
+ }
4822
+ }],
4823
+
4824
+ // jQuery.Color( )
4825
+ color = jQuery.Color = function( color, green, blue, alpha ) {
4826
+ return new jQuery.Color.fn.parse( color, green, blue, alpha );
4827
+ },
4828
+ spaces = {
4829
+ rgba: {
4830
+ props: {
4831
+ red: {
4832
+ idx: 0,
4833
+ type: "byte"
4834
+ },
4835
+ green: {
4836
+ idx: 1,
4837
+ type: "byte"
4838
+ },
4839
+ blue: {
4840
+ idx: 2,
4841
+ type: "byte"
4842
+ }
4843
+ }
4844
+ },
4845
 
4846
+ hsla: {
4847
+ props: {
4848
+ hue: {
4849
+ idx: 0,
4850
+ type: "degrees"
4851
+ },
4852
+ saturation: {
4853
+ idx: 1,
4854
+ type: "percent"
4855
+ },
4856
+ lightness: {
4857
+ idx: 2,
4858
+ type: "percent"
4859
+ }
4860
+ }
4861
+ }
4862
+ },
4863
+ propTypes = {
4864
+ "byte": {
4865
+ floor: true,
4866
+ max: 255
4867
  },
4868
+ "percent": {
4869
+ max: 1
4870
+ },
4871
+ "degrees": {
4872
+ mod: 360,
4873
+ floor: true
4874
  }
4875
  },
4876
+ support = color.support = {},
4877
 
4878
+ // element for support tests
4879
+ supportElem = jQuery( "<p>" )[ 0 ],
 
4880
 
4881
+ // colors = jQuery.Color.names
4882
+ colors,
4883
 
4884
+ // local aliases of functions called often
4885
+ each = jQuery.each;
 
 
 
 
4886
 
4887
+ // determine rgba support immediately
4888
+ supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
4889
+ support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
4890
+
4891
+ // define cache name and alpha properties
4892
+ // for rgba and hsla spaces
4893
+ each( spaces, function( spaceName, space ) {
4894
+ space.cache = "_" + spaceName;
4895
+ space.props.alpha = {
4896
+ idx: 3,
4897
+ type: "percent",
4898
+ def: 1
4899
+ };
4900
+ });
 
 
 
 
 
 
 
 
 
 
 
 
4901
 
4902
+ function clamp( value, prop, allowEmpty ) {
4903
+ var type = propTypes[ prop.type ] || {};
4904
 
4905
+ if ( value == null ) {
4906
+ return (allowEmpty || !prop.def) ? null : prop.def;
4907
+ }
4908
+
4909
+ // ~~ is an short way of doing floor for positive numbers
4910
+ value = type.floor ? ~~value : parseFloat( value );
4911
+
4912
+ // IE will pass in empty strings as value for alpha,
4913
+ // which will hit this case
4914
+ if ( isNaN( value ) ) {
4915
+ return prop.def;
4916
+ }
4917
+
4918
+ if ( type.mod ) {
4919
+ // we add mod before modding to make sure that negatives values
4920
+ // get converted properly: -10 -> 350
4921
+ return (value + type.mod) % type.mod;
4922
+ }
4923
+
4924
+ // for now all property types without mod have min and max
4925
+ return 0 > value ? 0 : type.max < value ? type.max : value;
4926
+ }
4927
+
4928
+ function stringParse( string ) {
4929
+ var inst = color(),
4930
+ rgba = inst._rgba = [];
4931
+
4932
+ string = string.toLowerCase();
4933
+
4934
+ each( stringParsers, function( i, parser ) {
4935
+ var parsed,
4936
+ match = parser.re.exec( string ),
4937
+ values = match && parser.parse( match ),
4938
+ spaceName = parser.space || "rgba";
4939
+
4940
+ if ( values ) {
4941
+ parsed = inst[ spaceName ]( values );
4942
+
4943
+ // if this was an rgba parse the assignment might happen twice
4944
+ // oh well....
4945
+ inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
4946
+ rgba = inst._rgba = parsed._rgba;
4947
+
4948
+ // exit each( stringParsers ) here because we matched
4949
+ return false;
4950
  }
4951
+ });
4952
 
4953
+ // Found a stringParser that handled it
4954
+ if ( rgba.length ) {
 
 
 
4955
 
4956
+ // if this came from a parsed string, force "transparent" when alpha is 0
4957
+ // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
4958
+ if ( rgba.join() === "0,0,0,0" ) {
4959
+ jQuery.extend( rgba, colors.transparent );
4960
+ }
4961
+ return inst;
4962
+ }
4963
 
4964
+ // named colors
4965
+ return colors[ string ];
4966
+ }
 
 
 
 
4967
 
4968
+ color.fn = jQuery.extend( color.prototype, {
4969
+ parse: function( red, green, blue, alpha ) {
4970
+ if ( red === undefined ) {
4971
+ this._rgba = [ null, null, null, null ];
4972
+ return this;
4973
+ }
4974
+ if ( red.jquery || red.nodeType ) {
4975
+ red = jQuery( red ).css( green );
4976
+ green = undefined;
4977
+ }
4978
 
4979
+ var inst = this,
4980
+ type = jQuery.type( red ),
4981
+ rgba = this._rgba = [];
4982
+
4983
+ // more than 1 argument specified - assume ( red, green, blue, alpha )
4984
+ if ( green !== undefined ) {
4985
+ red = [ red, green, blue, alpha ];
4986
+ type = "array";
 
 
4987
  }
4988
 
4989
+ if ( type === "string" ) {
4990
+ return this.parse( stringParse( red ) || colors._default );
 
4991
  }
4992
 
4993
+ if ( type === "array" ) {
4994
+ each( spaces.rgba.props, function( key, prop ) {
4995
+ rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
 
4996
  });
4997
+ return this;
4998
  }
 
4999
 
5000
+ if ( type === "object" ) {
5001
+ if ( red instanceof color ) {
5002
+ each( spaces, function( spaceName, space ) {
5003
+ if ( red[ space.cache ] ) {
5004
+ inst[ space.cache ] = red[ space.cache ].slice();
5005
+ }
5006
+ });
5007
+ } else {
5008
+ each( spaces, function( spaceName, space ) {
5009
+ var cache = space.cache;
5010
+ each( space.props, function( key, prop ) {
5011
+
5012
+ // if the cache doesn't exist, and we know how to convert
5013
+ if ( !inst[ cache ] && space.to ) {
5014
+
5015
+ // if the value was null, we don't need to copy it
5016
+ // if the key was alpha, we don't need to copy it either
5017
+ if ( key === "alpha" || red[ key ] == null ) {
5018
+ return;
5019
+ }
5020
+ inst[ cache ] = space.to( inst._rgba );
5021
+ }
5022
+
5023
+ // this is the only case where we allow nulls for ALL properties.
5024
+ // call clamp with alwaysAllowEmpty
5025
+ inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
5026
+ });
5027
+
5028
+ // everything defined but alpha?
5029
+ if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
5030
+ // use the default of 1
5031
+ inst[ cache ][ 3 ] = 1;
5032
+ if ( space.from ) {
5033
+ inst._rgba = space.from( inst[ cache ] );
5034
+ }
5035
+ }
5036
+ });
5037
+ }
5038
+ return this;
5039
  }
5040
  },
5041
+ is: function( compare ) {
5042
+ var is = color( compare ),
5043
+ same = true,
5044
+ inst = this;
5045
 
5046
+ each( spaces, function( _, space ) {
5047
+ var localCache,
5048
+ isCache = is[ space.cache ];
5049
+ if (isCache) {
5050
+ localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
5051
+ each( space.props, function( _, prop ) {
5052
+ if ( isCache[ prop.idx ] != null ) {
5053
+ same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
5054
+ return same;
5055
+ }
5056
+ });
5057
+ }
5058
+ return same;
5059
+ });
5060
+ return same;
5061
  },
5062
+ _space: function() {
5063
+ var used = [],
5064
+ inst = this;
5065
+ each( spaces, function( spaceName, space ) {
5066
+ if ( inst[ space.cache ] ) {
5067
+ used.push( spaceName );
5068
+ }
5069
+ });
5070
+ return used.pop();
5071
+ },
5072
+ transition: function( other, distance ) {
5073
+ var end = color( other ),
5074
+ spaceName = end._space(),
5075
+ space = spaces[ spaceName ],
5076
+ startColor = this.alpha() === 0 ? color( "transparent" ) : this,
5077
+ start = startColor[ space.cache ] || space.to( startColor._rgba ),
5078
+ result = start.slice();
5079
+
5080
+ end = end[ space.cache ];
5081
+ each( space.props, function( key, prop ) {
5082
+ var index = prop.idx,
5083
+ startValue = start[ index ],
5084
+ endValue = end[ index ],
5085
+ type = propTypes[ prop.type ] || {};
5086
+
5087
+ // if null, don't override start value
5088
+ if ( endValue === null ) {
5089
+ return;
5090
+ }
5091
+ // if null - use end
5092
+ if ( startValue === null ) {
5093
+ result[ index ] = endValue;
5094
+ } else {
5095
+ if ( type.mod ) {
5096
+ if ( endValue - startValue > type.mod / 2 ) {
5097
+ startValue += type.mod;
5098
+ } else if ( startValue - endValue > type.mod / 2 ) {
5099
+ startValue -= type.mod;
5100
+ }
5101
+ }
5102
+ result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
5103
+ }
5104
+ });
5105
+ return this[ spaceName ]( result );
5106
+ },
5107
+ blend: function( opaque ) {
5108
+ // if we are already opaque - return ourself
5109
+ if ( this._rgba[ 3 ] === 1 ) {
5110
+ return this;
5111
+ }
5112
 
5113
+ var rgb = this._rgba.slice(),
5114
+ a = rgb.pop(),
5115
+ blend = color( opaque )._rgba;
 
 
 
5116
 
5117
+ return color( jQuery.map( rgb, function( v, i ) {
5118
+ return ( 1 - a ) * blend[ i ] + a * v;
5119
+ }));
5120
+ },
5121
+ toRgbaString: function() {
5122
+ var prefix = "rgba(",
5123
+ rgba = jQuery.map( this._rgba, function( v, i ) {
5124
+ return v == null ? ( i > 2 ? 1 : 0 ) : v;
5125
+ });
5126
 
5127
+ if ( rgba[ 3 ] === 1 ) {
5128
+ rgba.pop();
5129
+ prefix = "rgb(";
 
 
 
 
 
5130
  }
5131
 
5132
+ return prefix + rgba.join() + ")";
5133
  },
5134
+ toHslaString: function() {
5135
+ var prefix = "hsla(",
5136
+ hsla = jQuery.map( this.hsla(), function( v, i ) {
5137
+ if ( v == null ) {
5138
+ v = i > 2 ? 1 : 0;
5139
+ }
5140
 
5141
+ // catch 1 and 2
5142
+ if ( i && i < 3 ) {
5143
+ v = Math.round( v * 100 ) + "%";
5144
+ }
5145
+ return v;
5146
+ });
5147
+
5148
+ if ( hsla[ 3 ] === 1 ) {
5149
+ hsla.pop();
5150
+ prefix = "hsl(";
 
 
 
 
 
 
 
 
5151
  }
5152
+ return prefix + hsla.join() + ")";
5153
  },
5154
+ toHexString: function( includeAlpha ) {
5155
+ var rgba = this._rgba.slice(),
5156
+ alpha = rgba.pop();
5157
 
5158
+ if ( includeAlpha ) {
5159
+ rgba.push( ~~( alpha * 255 ) );
 
5160
  }
5161
 
5162
+ return "#" + jQuery.map( rgba, function( v ) {
 
 
 
5163
 
5164
+ // default to 0 when nulls exist
5165
+ v = ( v || 0 ).toString( 16 );
5166
+ return v.length === 1 ? "0" + v : v;
5167
+ }).join("");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5168
  },
5169
+ toString: function() {
5170
+ return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
5171
+ }
5172
+ });
5173
+ color.fn.parse.prototype = color.fn;
5174
 
5175
+ // hsla conversions adapted from:
5176
+ // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
 
5177
 
5178
+ function hue2rgb( p, q, h ) {
5179
+ h = ( h + 1 ) % 1;
5180
+ if ( h * 6 < 1 ) {
5181
+ return p + (q - p) * h * 6;
5182
+ }
5183
+ if ( h * 2 < 1) {
5184
+ return q;
5185
+ }
5186
+ if ( h * 3 < 2 ) {
5187
+ return p + (q - p) * ((2/3) - h) * 6;
5188
+ }
5189
+ return p;
5190
+ }
5191
 
5192
+ spaces.hsla.to = function ( rgba ) {
5193
+ if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
5194
+ return [ null, null, null, rgba[ 3 ] ];
5195
+ }
5196
+ var r = rgba[ 0 ] / 255,
5197
+ g = rgba[ 1 ] / 255,
5198
+ b = rgba[ 2 ] / 255,
5199
+ a = rgba[ 3 ],
5200
+ max = Math.max( r, g, b ),
5201
+ min = Math.min( r, g, b ),
5202
+ diff = max - min,
5203
+ add = max + min,
5204
+ l = add * 0.5,
5205
+ h, s;
5206
+
5207
+ if ( min === max ) {
5208
+ h = 0;
5209
+ } else if ( r === max ) {
5210
+ h = ( 60 * ( g - b ) / diff ) + 360;
5211
+ } else if ( g === max ) {
5212
+ h = ( 60 * ( b - r ) / diff ) + 120;
5213
+ } else {
5214
+ h = ( 60 * ( r - g ) / diff ) + 240;
5215
+ }
5216
 
5217
+ // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
5218
+ // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
5219
+ if ( diff === 0 ) {
5220
+ s = 0;
5221
+ } else if ( l <= 0.5 ) {
5222
+ s = diff / add;
5223
+ } else {
5224
+ s = diff / ( 2 - add );
5225
+ }
5226
+ return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
5227
+ };
 
 
 
5228
 
5229
+ spaces.hsla.from = function ( hsla ) {
5230
+ if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
5231
+ return [ null, null, null, hsla[ 3 ] ];
5232
+ }
5233
+ var h = hsla[ 0 ] / 360,
5234
+ s = hsla[ 1 ],
5235
+ l = hsla[ 2 ],
5236
+ a = hsla[ 3 ],
5237
+ q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
5238
+ p = 2 * l - q;
5239
+
5240
+ return [
5241
+ Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
5242
+ Math.round( hue2rgb( p, q, h ) * 255 ),
5243
+ Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
5244
+ a
5245
+ ];
5246
+ };
5247
 
 
 
 
 
 
 
5248
 
5249
+ each( spaces, function( spaceName, space ) {
5250
+ var props = space.props,
5251
+ cache = space.cache,
5252
+ to = space.to,
5253
+ from = space.from;
5254
 
5255
+ // makes rgba() and hsla()
5256
+ color.fn[ spaceName ] = function( value ) {
 
 
 
 
 
 
 
5257
 
5258
+ // generate a cache for this space if it doesn't exist
5259
+ if ( to && !this[ cache ] ) {
5260
+ this[ cache ] = to( this._rgba );
 
 
5261
  }
5262
+ if ( value === undefined ) {
5263
+ return this[ cache ].slice();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5264
  }
5265
 
5266
+ var ret,
5267
+ type = jQuery.type( value ),
5268
+ arr = ( type === "array" || type === "object" ) ? value : arguments,
5269
+ local = this[ cache ].slice();
5270
+
5271
+ each( props, function( key, prop ) {
5272
+ var val = arr[ type === "object" ? key : prop.idx ];
5273
+ if ( val == null ) {
5274
+ val = local[ prop.idx ];
5275
+ }
5276
+ local[ prop.idx ] = clamp( val, prop );
5277
+ });
5278
 
5279
+ if ( from ) {
5280
+ ret = color( from( local ) );
5281
+ ret[ cache ] = local;
5282
+ return ret;
5283
+ } else {
5284
+ return color( local );
5285
+ }
5286
+ };
5287
 
5288
+ // makes red() green() blue() alpha() hue() saturation() lightness()
5289
+ each( props, function( key, prop ) {
5290
+ // alpha is included in more than one space
5291
+ if ( color.fn[ key ] ) {
5292
  return;
5293
  }
5294
+ color.fn[ key ] = function( value ) {
5295
+ var vtype = jQuery.type( value ),
5296
+ fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
5297
+ local = this[ fn ](),
5298
+ cur = local[ prop.idx ],
5299
+ match;
5300
 
5301
+ if ( vtype === "undefined" ) {
5302
+ return cur;
5303
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5304
 
5305
+ if ( vtype === "function" ) {
5306
+ value = value.call( this, cur );
5307
+ vtype = jQuery.type( value );
5308
+ }
5309
+ if ( value == null && prop.empty ) {
5310
+ return this;
5311
+ }
5312
+ if ( vtype === "string" ) {
5313
+ match = rplusequals.exec( value );
5314
+ if ( match ) {
5315
+ value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
5316
+ }
5317
+ }
5318
+ local[ prop.idx ] = value;
5319
+ return this[ fn ]( local );
5320
+ };
5321
+ });
5322
+ });
5323
 
5324
+ // add cssHook and .fx.step function for each named hook.
5325
+ // accept a space separated string of properties
5326
+ color.hook = function( hook ) {
5327
+ var hooks = hook.split( " " );
5328
+ each( hooks, function( i, hook ) {
5329
+ jQuery.cssHooks[ hook ] = {
5330
+ set: function( elem, value ) {
5331
+ var parsed, curElem,
5332
+ backgroundColor = "";
5333
+
5334
+ if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
5335
+ value = color( parsed || value );
5336
+ if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
5337
+ curElem = hook === "backgroundColor" ? elem.parentNode : elem;
5338
+ while (
5339
+ (backgroundColor === "" || backgroundColor === "transparent") &&
5340
+ curElem && curElem.style
5341
+ ) {
5342
+ try {
5343
+ backgroundColor = jQuery.css( curElem, "backgroundColor" );
5344
+ curElem = curElem.parentNode;
5345
+ } catch ( e ) {
5346
+ }
5347
+ }
5348
 
5349
+ value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
5350
+ backgroundColor :
5351
+ "_default" );
5352
+ }
5353
 
5354
+ value = value.toRgbaString();
5355
+ }
5356
+ try {
5357
+ elem.style[ hook ] = value;
5358
+ } catch( e ) {
5359
+ // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
5360
+ }
5361
  }
 
5362
  };
5363
+ jQuery.fx.step[ hook ] = function( fx ) {
5364
+ if ( !fx.colorInit ) {
5365
+ fx.start = color( fx.elem, hook );
5366
+ fx.end = color( fx.end );
5367
+ fx.colorInit = true;
5368
+ }
5369
+ jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
5370
+ };
5371
+ });
5372
 
5373
+ };
 
5374
 
5375
+ color.hook( stepHooks );
 
5376
 
5377
+ jQuery.cssHooks.borderColor = {
5378
+ expand: function( value ) {
5379
+ var expanded = {};
5380
 
5381
+ each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
5382
+ expanded[ "border" + part + "Color" ] = value;
5383
+ });
5384
+ return expanded;
5385
+ }
5386
+ };
 
 
 
 
 
 
 
 
 
 
 
5387
 
5388
+ // Basic color names only.
5389
+ // Usage of any of the other color names requires adding yourself or including
5390
+ // jquery.color.svg-names.js.
5391
+ colors = jQuery.Color.names = {
5392
+ // 4.1. Basic color keywords
5393
+ aqua: "#00ffff",
5394
+ black: "#000000",
5395
+ blue: "#0000ff",
5396
+ fuchsia: "#ff00ff",
5397
+ gray: "#808080",
5398
+ green: "#008000",
5399
+ lime: "#00ff00",
5400
+ maroon: "#800000",
5401
+ navy: "#000080",
5402
+ olive: "#808000",
5403
+ purple: "#800080",
5404
+ red: "#ff0000",
5405
+ silver: "#c0c0c0",
5406
+ teal: "#008080",
5407
+ white: "#ffffff",
5408
+ yellow: "#ffff00",
5409
+
5410
+ // 4.2.3. "transparent" color keyword
5411
+ transparent: [ null, null, null, 0 ],
5412
+
5413
+ _default: "#ffffff"
5414
+ };
5415
 
5416
+ })( jQuery );
5417
+
5418
+
5419
+ /******************************************************************************/
5420
+ /****************************** CLASS ANIMATIONS ******************************/
5421
+ /******************************************************************************/
5422
+ (function() {
5423
 
5424
+ var classAnimationActions = [ "add", "remove", "toggle" ],
5425
+ shorthandStyles = {
5426
+ border: 1,
5427
+ borderBottom: 1,
5428
+ borderColor: 1,
5429
+ borderLeft: 1,
5430
+ borderRight: 1,
5431
+ borderTop: 1,
5432
+ borderWidth: 1,
5433
+ margin: 1,
5434
+ padding: 1
5435
+ };
5436
 
5437
+ $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
5438
+ $.fx.step[ prop ] = function( fx ) {
5439
+ if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
5440
+ jQuery.style( fx.elem, prop, fx.end );
5441
+ fx.setAttr = true;
5442
+ }
5443
+ };
5444
+ });
5445
 
5446
+ function getElementStyles( elem ) {
5447
+ var key, len,
5448
+ style = elem.ownerDocument.defaultView ?
5449
+ elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
5450
+ elem.currentStyle,
5451
+ styles = {};
5452
 
5453
+ if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
5454
+ len = style.length;
5455
+ while ( len-- ) {
5456
+ key = style[ len ];
5457
+ if ( typeof style[ key ] === "string" ) {
5458
+ styles[ $.camelCase( key ) ] = style[ key ];
5459
  }
5460
+ }
5461
+ // support: Opera, IE <9
5462
+ } else {
5463
+ for ( key in style ) {
5464
+ if ( typeof style[ key ] === "string" ) {
5465
+ styles[ key ] = style[ key ];
 
5466
  }
5467
+ }
5468
+ }
5469
 
5470
+ return styles;
5471
+ }
 
 
 
 
 
 
5472
 
 
 
5473
 
5474
+ function styleDifference( oldStyle, newStyle ) {
5475
+ var diff = {},
5476
+ name, value;
 
 
 
 
 
 
 
 
 
 
 
 
 
5477
 
5478
+ for ( name in newStyle ) {
5479
+ value = newStyle[ name ];
5480
+ if ( oldStyle[ name ] !== value ) {
5481
+ if ( !shorthandStyles[ name ] ) {
5482
+ if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
5483
+ diff[ name ] = value;
5484
+ }
5485
+ }
5486
  }
5487
+ }
5488
 
5489
+ return diff;
5490
+ }
 
 
 
 
5491
 
5492
+ // support: jQuery <1.8
5493
+ if ( !$.fn.addBack ) {
5494
+ $.fn.addBack = function( selector ) {
5495
+ return this.add( selector == null ?
5496
+ this.prevObject : this.prevObject.filter( selector )
5497
+ );
5498
+ };
5499
+ }
5500
 
5501
+ $.effects.animateClass = function( value, duration, easing, callback ) {
5502
+ var o = $.speed( duration, easing, callback );
 
5503
 
5504
+ return this.queue( function() {
5505
+ var animated = $( this ),
5506
+ baseClass = animated.attr( "class" ) || "",
5507
+ applyClassChange,
5508
+ allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5509
 
5510
+ // map the animated objects to store the original styles.
5511
+ allAnimations = allAnimations.map(function() {
5512
+ var el = $( this );
5513
+ return {
5514
+ el: el,
5515
+ start: getElementStyles( this )
5516
+ };
5517
+ });
5518
+
5519
+ // apply class change
5520
+ applyClassChange = function() {
5521
+ $.each( classAnimationActions, function(i, action) {
5522
+ if ( value[ action ] ) {
5523
+ animated[ action + "Class" ]( value[ action ] );
 
5524
  }
5525
  });
5526
+ };
5527
+ applyClassChange();
5528
+
5529
+ // map all animated objects again - calculate new styles and diff
5530
+ allAnimations = allAnimations.map(function() {
5531
+ this.end = getElementStyles( this.el[ 0 ] );
5532
+ this.diff = styleDifference( this.start, this.end );
5533
+ return this;
5534
+ });
5535
+
5536
+ // apply original class
5537
+ animated.attr( "class", baseClass );
5538
+
5539
+ // map all animated objects again - this time collecting a promise
5540
+ allAnimations = allAnimations.map(function() {
5541
+ var styleInfo = this,
5542
+ dfd = $.Deferred(),
5543
+ opts = $.extend({}, o, {
5544
+ queue: false,
5545
+ complete: function() {
5546
+ dfd.resolve( styleInfo );
5547
+ }
5548
+ });
5549
+
5550
+ this.el.animate( this.diff, opts );
5551
+ return dfd.promise();
5552
+ });
5553
+
5554
+ // once all animations have completed:
5555
+ $.when.apply( $, allAnimations.get() ).done(function() {
5556
+
5557
+ // set the final class
5558
+ applyClassChange();
5559
+
5560
+ // for each animated element,
5561
+ // clear all css properties that were animated
5562
+ $.each( arguments, function() {
5563
+ var el = this.el;
5564
+ $.each( this.diff, function(key) {
5565
+ el.css( key, "" );
5566
+ });
5567
  });
5568
+
5569
+ // this is guarnteed to be there if you use jQuery.speed()
5570
+ // it also handles dequeuing the next anim...
5571
+ o.complete.call( animated[ 0 ] );
5572
+ });
5573
+ });
5574
+ };
5575
+
5576
+ $.fn.extend({
5577
+ addClass: (function( orig ) {
5578
+ return function( classNames, speed, easing, callback ) {
5579
+ return speed ?
5580
+ $.effects.animateClass.call( this,
5581
+ { add: classNames }, speed, easing, callback ) :
5582
+ orig.apply( this, arguments );
5583
+ };
5584
+ })( $.fn.addClass ),
5585
+
5586
+ removeClass: (function( orig ) {
5587
+ return function( classNames, speed, easing, callback ) {
5588
+ return arguments.length > 1 ?
5589
+ $.effects.animateClass.call( this,
5590
+ { remove: classNames }, speed, easing, callback ) :
5591
+ orig.apply( this, arguments );
5592
+ };
5593
+ })( $.fn.removeClass ),
5594
+
5595
+ toggleClass: (function( orig ) {
5596
+ return function( classNames, force, speed, easing, callback ) {
5597
+ if ( typeof force === "boolean" || force === undefined ) {
5598
+ if ( !speed ) {
5599
+ // without speed parameter
5600
+ return orig.apply( this, arguments );
5601
+ } else {
5602
+ return $.effects.animateClass.call( this,
5603
+ (force ? { add: classNames } : { remove: classNames }),
5604
+ speed, easing, callback );
5605
+ }
5606
+ } else {
5607
+ // without force parameter
5608
+ return $.effects.animateClass.call( this,
5609
+ { toggle: classNames }, force, speed, easing );
5610
+ }
5611
+ };
5612
+ })( $.fn.toggleClass ),
5613
+
5614
+ switchClass: function( remove, add, speed, easing, callback) {
5615
+ return $.effects.animateClass.call( this, {
5616
+ add: add,
5617
+ remove: remove
5618
+ }, speed, easing, callback );
5619
  }
5620
  });
5621
 
5622
+ })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5623
 
5624
+ /******************************************************************************/
5625
+ /*********************************** EFFECTS **********************************/
5626
+ /******************************************************************************/
5627
 
5628
+ (function() {
 
 
 
 
 
 
 
 
 
 
 
 
5629
 
5630
+ $.extend( $.effects, {
5631
+ version: "1.10.2",
5632
 
5633
+ // Saves a set of properties in a data storage
5634
+ save: function( element, set ) {
5635
+ for( var i=0; i < set.length; i++ ) {
5636
+ if ( set[ i ] !== null ) {
5637
+ element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
5638
+ }
5639
+ }
5640
+ },
5641
 
5642
+ // Restores a set of previously saved properties from a data storage
5643
+ restore: function( element, set ) {
5644
+ var val, i;
5645
+ for( i=0; i < set.length; i++ ) {
5646
+ if ( set[ i ] !== null ) {
5647
+ val = element.data( dataSpace + set[ i ] );
5648
+ // support: jQuery 1.6.2
5649
+ // http://bugs.jquery.com/ticket/9917
5650
+ // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
5651
+ // We can't differentiate between "" and 0 here, so we just assume
5652
+ // empty string since it's likely to be a more common value...
5653
+ if ( val === undefined ) {
5654
+ val = "";
5655
  }
5656
+ element.css( set[ i ], val );
5657
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5658
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5659
  },
5660
 
5661
+ setMode: function( el, mode ) {
5662
+ if (mode === "toggle") {
5663
+ mode = el.is( ":hidden" ) ? "show" : "hide";
 
 
 
 
 
 
 
5664
  }
5665
+ return mode;
5666
  },
5667
 
5668
+ // Translates a [top,left] array into a baseline value
5669
+ // this should be a little more flexible in the future to handle a string & hash
5670
+ getBaseline: function( origin, original ) {
5671
+ var y, x;
5672
+ switch ( origin[ 0 ] ) {
5673
+ case "top": y = 0; break;
5674
+ case "middle": y = 0.5; break;
5675
+ case "bottom": y = 1; break;
5676
+ default: y = origin[ 0 ] / original.height;
5677
+ }
5678
+ switch ( origin[ 1 ] ) {
5679
+ case "left": x = 0; break;
5680
+ case "center": x = 0.5; break;
5681
+ case "right": x = 1; break;
5682
+ default: x = origin[ 1 ] / original.width;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5683
  }
5684
+ return {
5685
+ x: x,
5686
+ y: y
5687
+ };
5688
  },
5689
 
5690
+ // Wraps the element around a wrapper that copies position properties
5691
+ createWrapper: function( element ) {
 
 
 
5692
 
5693
+ // if the element is already wrapped, return it
5694
+ if ( element.parent().is( ".ui-effects-wrapper" )) {
5695
+ return element.parent();
5696
  }
5697
 
5698
+ // wrap the element
5699
+ var props = {
5700
+ width: element.outerWidth(true),
5701
+ height: element.outerHeight(true),
5702
+ "float": element.css( "float" )
5703
+ },
5704
+ wrapper = $( "<div></div>" )
5705
+ .addClass( "ui-effects-wrapper" )
5706
+ .css({
5707
+ fontSize: "100%",
5708
+ background: "transparent",
5709
+ border: "none",
5710
+ margin: 0,
5711
+ padding: 0
5712
+ }),
5713
+ // Store the size in case width/height are defined in % - Fixes #5245
5714
+ size = {
5715
+ width: element.width(),
5716
+ height: element.height()
5717
+ },
5718
+ active = document.activeElement;
5719
+
5720
+ // support: Firefox
5721
+ // Firefox incorrectly exposes anonymous content
5722
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
5723
+ try {
5724
+ active.id;
5725
+ } catch( e ) {
5726
+ active = document.body;
5727
  }
5728
 
5729
+ element.wrap( wrapper );
 
5730
 
5731
+ // Fixes #7595 - Elements lose focus when wrapped.
5732
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5733
+ $( active ).focus();
5734
+ }
5735
 
5736
+ wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
 
5737
 
5738
+ // transfer positioning properties to the wrapper
5739
+ if ( element.css( "position" ) === "static" ) {
5740
+ wrapper.css({ position: "relative" });
5741
+ element.css({ position: "relative" });
 
5742
  } else {
5743
+ $.extend( props, {
5744
+ position: element.css( "position" ),
5745
+ zIndex: element.css( "z-index" )
5746
+ });
5747
+ $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
5748
+ props[ pos ] = element.css( pos );
5749
+ if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
5750
+ props[ pos ] = "auto";
5751
+ }
5752
+ });
5753
+ element.css({
5754
+ position: "relative",
5755
+ top: 0,
5756
+ left: 0,
5757
+ right: "auto",
5758
+ bottom: "auto"
5759
+ });
 
 
 
5760
  }
5761
+ element.css(size);
5762
 
5763
+ return wrapper.css( props ).show();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5764
  },
5765
 
5766
+ removeWrapper: function( element ) {
5767
+ var active = document.activeElement;
 
 
 
 
 
 
5768
 
5769
+ if ( element.parent().is( ".ui-effects-wrapper" ) ) {
5770
+ element.parent().replaceWith( element );
 
 
 
 
5771
 
5772
+ // Fixes #7595 - Elements lose focus when wrapped.
5773
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5774
+ $( active ).focus();
5775
+ }
5776
  }
 
5777
 
5778
+
5779
+ return element;
 
 
 
 
 
 
5780
  },
5781
 
5782
+ setTransition: function( element, list, factor, value ) {
5783
+ value = value || {};
5784
+ $.each( list, function( i, x ) {
5785
+ var unit = element.cssUnit( x );
5786
+ if ( unit[ 0 ] > 0 ) {
5787
+ value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
5788
+ }
5789
  });
5790
+ return value;
5791
+ }
5792
+ });
5793
 
5794
+ // return an effect options object for the given parameters:
5795
+ function _normalizeArguments( effect, options, speed, callback ) {
 
 
 
 
5796
 
5797
+ // allow passing all options as the first parameter
5798
+ if ( $.isPlainObject( effect ) ) {
5799
+ options = effect;
5800
+ effect = effect.effect;
5801
+ }
 
 
 
 
 
 
 
 
5802
 
5803
+ // convert to an object
5804
+ effect = { effect: effect };
5805
+
5806
+ // catch (effect, null, ...)
5807
+ if ( options == null ) {
5808
+ options = {};
5809
  }
 
5810
 
5811
+ // catch (effect, callback)
5812
+ if ( $.isFunction( options ) ) {
5813
+ callback = options;
5814
+ speed = null;
5815
+ options = {};
 
 
 
 
5816
  }
 
5817
 
5818
+ // catch (effect, speed, ?)
5819
+ if ( typeof options === "number" || $.fx.speeds[ options ] ) {
5820
+ callback = speed;
5821
+ speed = options;
5822
+ options = {};
5823
+ }
5824
 
5825
+ // catch (effect, options, callback)
5826
+ if ( $.isFunction( speed ) ) {
5827
+ callback = speed;
5828
+ speed = null;
5829
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
5830
 
5831
+ // add options to effect
5832
+ if ( options ) {
5833
+ $.extend( effect, options );
5834
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5835
 
5836
+ speed = speed || options.duration;
5837
+ effect.duration = $.fx.off ? 0 :
5838
+ typeof speed === "number" ? speed :
5839
+ speed in $.fx.speeds ? $.fx.speeds[ speed ] :
5840
+ $.fx.speeds._default;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5841
 
5842
+ effect.complete = callback || options.complete;
 
5843
 
5844
+ return effect;
5845
+ }
 
 
 
 
5846
 
5847
+ function standardAnimationOption( option ) {
5848
+ // Valid standard speeds (nothing, number, named speed)
5849
+ if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
5850
+ return true;
5851
+ }
5852
 
5853
+ // Invalid strings - treat as "normal" speed
5854
+ if ( typeof option === "string" && !$.effects.effect[ option ] ) {
5855
+ return true;
5856
+ }
5857
 
5858
+ // Complete callback
5859
+ if ( $.isFunction( option ) ) {
5860
+ return true;
5861
+ }
5862
 
5863
+ // Options hash (but not naming an effect)
5864
+ if ( typeof option === "object" && !option.effect ) {
5865
+ return true;
5866
+ }
5867
 
5868
+ // Didn't match any standard API
5869
+ return false;
5870
+ }
5871
+
5872
+ $.fn.extend({
5873
+ effect: function( /* effect, options, speed, callback */ ) {
5874
+ var args = _normalizeArguments.apply( this, arguments ),
5875
+ mode = args.mode,
5876
+ queue = args.queue,
5877
+ effectMethod = $.effects.effect[ args.effect ];
5878
+
5879
+ if ( $.fx.off || !effectMethod ) {
5880
+ // delegate to the original method (e.g., .show()) if possible
5881
+ if ( mode ) {
5882
+ return this[ mode ]( args.duration, args.complete );
5883
+ } else {
5884
+ return this.each( function() {
5885
+ if ( args.complete ) {
5886
+ args.complete.call( this );
5887
+ }
5888
+ });
5889
+ }
5890
  }
 
5891
 
5892
+ function run( next ) {
5893
+ var elem = $( this ),
5894
+ complete = args.complete,
5895
+ mode = args.mode;
5896
+
5897
+ function done() {
5898
+ if ( $.isFunction( complete ) ) {
5899
+ complete.call( elem[0] );
5900
+ }
5901
+ if ( $.isFunction( next ) ) {
5902
+ next();
5903
+ }
5904
  }
 
 
 
 
 
 
 
5905
 
5906
+ // If the element already has the correct final state, delegate to
5907
+ // the core methods so the internal tracking of "olddisplay" works.
5908
+ if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
5909
+ elem[ mode ]();
5910
+ done();
5911
+ } else {
5912
+ effectMethod.call( elem[0], args, done );
5913
  }
 
 
 
 
5914
  }
5915
+
5916
+ return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
5917
  },
5918
 
5919
+ show: (function( orig ) {
5920
+ return function( option ) {
5921
+ if ( standardAnimationOption( option ) ) {
5922
+ return orig.apply( this, arguments );
5923
+ } else {
5924
+ var args = _normalizeArguments.apply( this, arguments );
5925
+ args.mode = "show";
5926
+ return this.effect.call( this, args );
5927
  }
5928
+ };
5929
+ })( $.fn.show ),
5930
 
5931
+ hide: (function( orig ) {
5932
+ return function( option ) {
5933
+ if ( standardAnimationOption( option ) ) {
5934
+ return orig.apply( this, arguments );
5935
+ } else {
5936
+ var args = _normalizeArguments.apply( this, arguments );
5937
+ args.mode = "hide";
5938
+ return this.effect.call( this, args );
5939
+ }
5940
+ };
5941
+ })( $.fn.hide ),
5942
 
5943
+ toggle: (function( orig ) {
5944
+ return function( option ) {
5945
+ if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
5946
+ return orig.apply( this, arguments );
5947
+ } else {
5948
+ var args = _normalizeArguments.apply( this, arguments );
5949
+ args.mode = "toggle";
5950
+ return this.effect.call( this, args );
5951
  }
5952
+ };
5953
+ })( $.fn.toggle ),
 
 
 
 
5954
 
5955
+ // helper functions
5956
+ cssUnit: function(key) {
5957
+ var style = this.css( key ),
5958
+ val = [];
5959
 
5960
+ $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
5961
+ if ( style.indexOf( unit ) > 0 ) {
5962
+ val = [ parseFloat( style ), unit ];
5963
+ }
5964
+ });
5965
+ return val;
5966
  }
5967
  });
5968
 
5969
+ })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5970
 
5971
+ /******************************************************************************/
5972
+ /*********************************** EASING ***********************************/
5973
+ /******************************************************************************/
5974
+
5975
+ (function() {
5976
+
5977
+ // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
5978
+
5979
+ var baseEasings = {};
5980
+
5981
+ $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
5982
+ baseEasings[ name ] = function( p ) {
5983
+ return Math.pow( p, i + 2 );
5984
+ };
5985
+ });
5986
+
5987
+ $.extend( baseEasings, {
5988
+ Sine: function ( p ) {
5989
+ return 1 - Math.cos( p * Math.PI / 2 );
5990
  },
5991
+ Circ: function ( p ) {
5992
+ return 1 - Math.sqrt( 1 - p * p );
5993
+ },
5994
+ Elastic: function( p ) {
5995
+ return p === 0 || p === 1 ? p :
5996
+ -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
5997
+ },
5998
+ Back: function( p ) {
5999
+ return p * p * ( 3 * p - 2 );
6000
+ },
6001
+ Bounce: function ( p ) {
6002
+ var pow2,
6003
+ bounce = 4;
6004
+
6005
+ while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
6006
+ return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
6007
+ }
6008
+ });
6009
+
6010
+ $.each( baseEasings, function( name, easeIn ) {
6011
+ $.easing[ "easeIn" + name ] = easeIn;
6012
+ $.easing[ "easeOut" + name ] = function( p ) {
6013
+ return 1 - easeIn( 1 - p );
6014
+ };
6015
+ $.easing[ "easeInOut" + name ] = function( p ) {
6016
+ return p < 0.5 ?
6017
+ easeIn( p * 2 ) / 2 :
6018
+ 1 - easeIn( p * -2 + 2 ) / 2;
6019
  };
6020
+ });
6021
 
6022
+ })();
6023
+
6024
+ })(jQuery);
6025
+
6026
+ (function( $, undefined ) {
6027
+
6028
+ var uid = 0,
6029
+ hideProps = {},
6030
+ showProps = {};
6031
+
6032
+ hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
6033
+ hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
6034
+ showProps.height = showProps.paddingTop = showProps.paddingBottom =
6035
+ showProps.borderTopWidth = showProps.borderBottomWidth = "show";
6036
+
6037
+ $.widget( "ui.accordion", {
6038
+ version: "1.10.2",
6039
  options: {
6040
+ active: 0,
6041
+ animate: {},
6042
+ collapsible: false,
6043
+ event: "click",
6044
+ header: "> li > :first-child,> :not(li):even",
6045
+ heightStyle: "auto",
6046
  icons: {
6047
+ activeHeader: "ui-icon-triangle-1-s",
6048
+ header: "ui-icon-triangle-1-e"
6049
+ },
6050
+
6051
+ // callbacks
6052
+ activate: null,
6053
+ beforeActivate: null
6054
  },
6055
+
6056
  _create: function() {
6057
+ var options = this.options;
6058
+ this.prevShow = this.prevHide = $();
6059
+ this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
6060
+ // ARIA
6061
+ .attr( "role", "tablist" );
6062
 
6063
+ // don't allow collapsible: false and active: false / null
6064
+ if ( !options.collapsible && (options.active === false || options.active == null) ) {
6065
+ options.active = 0;
6066
  }
6067
 
6068
+ this._processPanels();
6069
+ // handle negative values
6070
+ if ( options.active < 0 ) {
6071
+ options.active += this.headers.length;
6072
+ }
6073
+ this._refresh();
6074
+ },
6075
 
6076
+ _getCreateEventData: function() {
6077
+ return {
6078
+ header: this.active,
6079
+ panel: !this.active.length ? $() : this.active.next(),
6080
+ content: !this.active.length ? $() : this.active.next()
6081
+ };
6082
+ },
6083
 
6084
+ _createIcons: function() {
6085
+ var icons = this.options.icons;
6086
+ if ( icons ) {
6087
+ $( "<span>" )
6088
+ .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
6089
+ .prependTo( this.headers );
6090
+ this.active.children( ".ui-accordion-header-icon" )
6091
+ .removeClass( icons.header )
6092
+ .addClass( icons.activeHeader );
6093
+ this.headers.addClass( "ui-accordion-icons" );
6094
  }
6095
+ },
6096
 
6097
+ _destroyIcons: function() {
6098
+ this.headers
6099
+ .removeClass( "ui-accordion-icons" )
6100
+ .children( ".ui-accordion-header-icon" )
6101
+ .remove();
6102
+ },
6103
 
6104
+ _destroy: function() {
6105
+ var contents;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6106
 
6107
+ // clean up main element
6108
  this.element
6109
+ .removeClass( "ui-accordion ui-widget ui-helper-reset" )
6110
+ .removeAttr( "role" );
 
 
 
 
 
6111
 
6112
+ // clean up headers
6113
+ this.headers
6114
+ .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
6115
+ .removeAttr( "role" )
6116
+ .removeAttr( "aria-selected" )
6117
+ .removeAttr( "aria-controls" )
6118
+ .removeAttr( "tabIndex" )
6119
+ .each(function() {
6120
+ if ( /^ui-accordion/.test( this.id ) ) {
6121
+ this.removeAttribute( "id" );
6122
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6123
  });
6124
+ this._destroyIcons();
6125
 
6126
+ // clean up content panels
6127
+ contents = this.headers.next()
6128
+ .css( "display", "" )
6129
+ .removeAttr( "role" )
6130
+ .removeAttr( "aria-expanded" )
6131
+ .removeAttr( "aria-hidden" )
6132
+ .removeAttr( "aria-labelledby" )
6133
+ .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
6134
+ .each(function() {
6135
+ if ( /^ui-accordion/.test( this.id ) ) {
6136
+ this.removeAttribute( "id" );
6137
  }
 
 
6138
  });
6139
+ if ( this.options.heightStyle !== "content" ) {
6140
+ contents.css( "height", "" );
6141
+ }
6142
+ },
 
 
 
6143
 
6144
+ _setOption: function( key, value ) {
6145
+ if ( key === "active" ) {
6146
+ // _activate() will handle invalid values and update this.options
6147
+ this._activate( value );
6148
+ return;
6149
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6150
 
6151
+ if ( key === "event" ) {
6152
+ if ( this.options.event ) {
6153
+ this._off( this.headers, this.options.event );
 
 
 
 
6154
  }
6155
+ this._setupEvents( value );
6156
  }
6157
 
6158
+ this._super( key, value );
 
 
 
 
 
 
 
6159
 
6160
+ // setting collapsible: false while collapsed; open first panel
6161
+ if ( key === "collapsible" && !value && this.options.active === false ) {
6162
+ this._activate( 0 );
 
 
 
 
 
6163
  }
6164
 
6165
+ if ( key === "icons" ) {
6166
+ this._destroyIcons();
6167
+ if ( value ) {
6168
+ this._createIcons();
 
 
 
 
 
 
 
 
6169
  }
6170
+ }
6171
 
6172
+ // #5332 - opacity doesn't cascade to positioned elements in IE
6173
+ // so we need to add the disabled class to the headers and panels
6174
+ if ( key === "disabled" ) {
6175
+ this.headers.add( this.headers.next() )
6176
+ .toggleClass( "ui-state-disabled", !!value );
 
 
6177
  }
6178
  },
6179
 
6180
+ _keydown: function( event ) {
6181
+ /*jshint maxcomplexity:15*/
6182
+ if ( event.altKey || event.ctrlKey ) {
6183
+ return;
6184
+ }
6185
 
6186
+ var keyCode = $.ui.keyCode,
6187
+ length = this.headers.length,
6188
+ currentIndex = this.headers.index( event.target ),
6189
+ toFocus = false;
 
 
 
 
6190
 
6191
+ switch ( event.keyCode ) {
6192
+ case keyCode.RIGHT:
6193
+ case keyCode.DOWN:
6194
+ toFocus = this.headers[ ( currentIndex + 1 ) % length ];
6195
+ break;
6196
+ case keyCode.LEFT:
6197
+ case keyCode.UP:
6198
+ toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
6199
+ break;
6200
+ case keyCode.SPACE:
6201
+ case keyCode.ENTER:
6202
+ this._eventHandler( event );
6203
+ break;
6204
+ case keyCode.HOME:
6205
+ toFocus = this.headers[ 0 ];
6206
+ break;
6207
+ case keyCode.END:
6208
+ toFocus = this.headers[ length - 1 ];
6209
+ break;
6210
  }
6211
 
6212
+ if ( toFocus ) {
6213
+ $( event.target ).attr( "tabIndex", -1 );
6214
+ $( toFocus ).attr( "tabIndex", 0 );
6215
+ toFocus.focus();
6216
+ event.preventDefault();
6217
+ }
6218
  },
6219
 
6220
+ _panelKeyDown : function( event ) {
6221
+ if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
6222
+ $( event.currentTarget ).prev().focus();
 
 
 
 
 
 
6223
  }
 
6224
  },
6225
 
6226
  refresh: function() {
6227
+ var options = this.options;
6228
+ this._processPanels();
6229
+
6230
+ // was collapsed or no panel
6231
+ if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
6232
+ options.active = false;
6233
+ this.active = $();
6234
+ // active false only when collapsible is true
6235
+ } if ( options.active === false ) {
6236
+ this._activate( 0 );
6237
+ // was active, but active panel is gone
6238
+ } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
6239
+ // all remaining panel are disabled
6240
+ if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
6241
+ options.active = false;
6242
+ this.active = $();
6243
+ // activate previous panel
 
 
 
 
6244
  } else {
6245
+ this._activate( Math.max( 0, options.active - 1 ) );
 
 
6246
  }
6247
+ // was active, active panel still exists
6248
+ } else {
6249
+ // make sure active index is correct
6250
+ options.active = this.headers.index( this.active );
6251
  }
 
6252
 
6253
+ this._destroyIcons();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6254
 
6255
+ this._refresh();
6256
+ },
 
 
6257
 
6258
+ _processPanels: function() {
6259
+ this.headers = this.element.find( this.options.header )
6260
+ .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
6261
 
6262
+ this.headers.next()
6263
+ .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
6264
+ .filter(":not(.ui-accordion-content-active)")
6265
+ .hide();
6266
+ },
6267
 
6268
+ _refresh: function() {
6269
+ var maxHeight,
6270
+ options = this.options,
6271
+ heightStyle = options.heightStyle,
6272
+ parent = this.element.parent(),
6273
+ accordionId = this.accordionId = "ui-accordion-" +
6274
+ (this.element.attr( "id" ) || ++uid);
6275
+
6276
+ this.active = this._findActive( options.active )
6277
+ .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
6278
+ .removeClass( "ui-corner-all" );
6279
+ this.active.next()
6280
+ .addClass( "ui-accordion-content-active" )
6281
+ .show();
6282
 
6283
+ this.headers
6284
+ .attr( "role", "tab" )
6285
+ .each(function( i ) {
6286
+ var header = $( this ),
6287
+ headerId = header.attr( "id" ),
6288
+ panel = header.next(),
6289
+ panelId = panel.attr( "id" );
6290
+ if ( !headerId ) {
6291
+ headerId = accordionId + "-header-" + i;
6292
+ header.attr( "id", headerId );
6293
  }
6294
+ if ( !panelId ) {
6295
+ panelId = accordionId + "-panel-" + i;
6296
+ panel.attr( "id", panelId );
6297
+ }
6298
+ header.attr( "aria-controls", panelId );
6299
+ panel.attr( "aria-labelledby", headerId );
6300
+ })
6301
+ .next()
6302
+ .attr( "role", "tabpanel" );
6303
+
6304
+ this.headers
6305
+ .not( this.active )
6306
+ .attr({
6307
+ "aria-selected": "false",
6308
+ tabIndex: -1
6309
+ })
6310
+ .next()
6311
+ .attr({
6312
+ "aria-expanded": "false",
6313
+ "aria-hidden": "true"
6314
+ })
6315
+ .hide();
6316
+
6317
+ // make sure at least one header is in the tab order
6318
+ if ( !this.active.length ) {
6319
+ this.headers.eq( 0 ).attr( "tabIndex", 0 );
6320
  } else {
6321
+ this.active.attr({
6322
+ "aria-selected": "true",
6323
+ tabIndex: 0
6324
+ })
6325
+ .next()
6326
+ .attr({
6327
+ "aria-expanded": "true",
6328
+ "aria-hidden": "false"
6329
+ });
6330
  }
 
 
 
6331
 
6332
+ this._createIcons();
6333
+
6334
+ this._setupEvents( options.event );
6335
+
6336
+ if ( heightStyle === "fill" ) {
6337
+ maxHeight = parent.height();
6338
+ this.element.siblings( ":visible" ).each(function() {
6339
+ var elem = $( this ),
6340
+ position = elem.css( "position" );
6341
+
6342
+ if ( position === "absolute" || position === "fixed" ) {
6343
+ return;
6344
+ }
6345
+ maxHeight -= elem.outerHeight( true );
6346
+ });
6347
+
6348
+ this.headers.each(function() {
6349
+ maxHeight -= $( this ).outerHeight( true );
6350
+ });
6351
+
6352
+ this.headers.next()
6353
+ .each(function() {
6354
+ $( this ).height( Math.max( 0, maxHeight -
6355
+ $( this ).innerHeight() + $( this ).height() ) );
6356
+ })
6357
+ .css( "overflow", "auto" );
6358
+ } else if ( heightStyle === "auto" ) {
6359
+ maxHeight = 0;
6360
+ this.headers.next()
6361
+ .each(function() {
6362
+ maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
6363
+ })
6364
+ .height( maxHeight );
6365
+ }
6366
  },
6367
 
6368
+ _activate: function( index ) {
6369
+ var active = this._findActive( index )[ 0 ];
6370
+
6371
+ // trying to activate the already active panel
6372
+ if ( active === this.active[ 0 ] ) {
6373
+ return;
6374
+ }
6375
+
6376
+ // trying to collapse, simulate a click on the currently active header
6377
+ active = active || this.active[ 0 ];
6378
+
6379
+ this._eventHandler({
6380
+ target: active,
6381
+ currentTarget: active,
6382
+ preventDefault: $.noop
6383
+ });
6384
  },
6385
+
6386
+ _findActive: function( selector ) {
6387
+ return typeof selector === "number" ? this.headers.eq( selector ) : $();
6388
  },
6389
 
6390
+ _setupEvents: function( event ) {
6391
+ var events = {
6392
+ keydown: "_keydown"
6393
+ };
6394
+ if ( event ) {
6395
+ $.each( event.split(" "), function( index, eventName ) {
6396
+ events[ eventName ] = "_eventHandler";
6397
+ });
6398
  }
6399
 
6400
+ this._off( this.headers.add( this.headers.next() ) );
6401
+ this._on( this.headers, events );
6402
+ this._on( this.headers.next(), { keydown: "_panelKeyDown" });
6403
+ this._hoverable( this.headers );
6404
+ this._focusable( this.headers );
6405
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6406
 
6407
+ _eventHandler: function( event ) {
6408
+ var options = this.options,
6409
+ active = this.active,
6410
+ clicked = $( event.currentTarget ),
6411
+ clickedIsActive = clicked[ 0 ] === active[ 0 ],
6412
+ collapsing = clickedIsActive && options.collapsible,
6413
+ toShow = collapsing ? $() : clicked.next(),
6414
+ toHide = active.next(),
6415
+ eventData = {
6416
+ oldHeader: active,
6417
+ oldPanel: toHide,
6418
+ newHeader: collapsing ? $() : clicked,
6419
+ newPanel: toShow
6420
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6421
 
6422
+ event.preventDefault();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6423
 
6424
+ if (
6425
+ // click on active header, but not collapsible
6426
+ ( clickedIsActive && !options.collapsible ) ||
6427
+ // allow canceling activation
6428
+ ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
6429
+ return;
6430
  }
6431
 
6432
+ options.active = collapsing ? false : this.headers.index( clicked );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6433
 
6434
+ // when the call to ._toggle() comes after the class changes
6435
+ // it causes a very odd bug in IE 8 (see #6720)
6436
+ this.active = clickedIsActive ? $() : clicked;
6437
+ this._toggle( eventData );
6438
 
6439
+ // switch classes
6440
+ // corner classes on the previously active header stay after the animation
6441
+ active.removeClass( "ui-accordion-header-active ui-state-active" );
6442
+ if ( options.icons ) {
6443
+ active.children( ".ui-accordion-header-icon" )
6444
+ .removeClass( options.icons.activeHeader )
6445
+ .addClass( options.icons.header );
6446
  }
6447
 
6448
+ if ( !clickedIsActive ) {
6449
+ clicked
6450
+ .removeClass( "ui-corner-all" )
6451
+ .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
6452
+ if ( options.icons ) {
6453
+ clicked.children( ".ui-accordion-header-icon" )
6454
+ .removeClass( options.icons.header )
6455
+ .addClass( options.icons.activeHeader );
6456
+ }
6457
 
6458
+ clicked
6459
+ .next()
6460
+ .addClass( "ui-accordion-content-active" );
6461
  }
6462
  },
6463
 
6464
+ _toggle: function( data ) {
6465
+ var toShow = data.newPanel,
6466
+ toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
 
 
6467
 
6468
+ // handle activating a panel during the animation for another activation
6469
+ this.prevShow.add( this.prevHide ).stop( true, true );
6470
+ this.prevShow = toShow;
6471
+ this.prevHide = toHide;
6472
+
6473
+ if ( this.options.animate ) {
6474
+ this._animate( toShow, toHide, data );
6475
+ } else {
6476
+ toHide.hide();
6477
+ toShow.show();
6478
+ this._toggleComplete( data );
6479
  }
 
 
 
 
 
 
 
6480
 
6481
+ toHide.attr({
6482
+ "aria-expanded": "false",
6483
+ "aria-hidden": "true"
6484
+ });
6485
+ toHide.prev().attr( "aria-selected", "false" );
6486
+ // if we're switching panels, remove the old header from the tab order
6487
+ // if we're opening from collapsed state, remove the previous header from the tab order
6488
+ // if we're collapsing, then keep the collapsing header in the tab order
6489
+ if ( toShow.length && toHide.length ) {
6490
+ toHide.prev().attr( "tabIndex", -1 );
6491
+ } else if ( toShow.length ) {
6492
+ this.headers.filter(function() {
6493
+ return $( this ).attr( "tabIndex" ) === 0;
6494
+ })
6495
+ .attr( "tabIndex", -1 );
6496
  }
6497
 
6498
+ toShow
6499
+ .attr({
6500
+ "aria-expanded": "true",
6501
+ "aria-hidden": "false"
6502
+ })
6503
+ .prev()
6504
+ .attr({
6505
+ "aria-selected": "true",
6506
+ tabIndex: 0
6507
+ });
6508
  },
6509
 
6510
+ _animate: function( toShow, toHide, data ) {
6511
+ var total, easing, duration,
6512
+ that = this,
6513
+ adjust = 0,
6514
+ down = toShow.length &&
6515
+ ( !toHide.length || ( toShow.index() < toHide.index() ) ),
6516
+ animate = this.options.animate || {},
6517
+ options = down && animate.down || animate,
6518
+ complete = function() {
6519
+ that._toggleComplete( data );
6520
+ };
6521
 
6522
+ if ( typeof options === "number" ) {
6523
+ duration = options;
 
 
 
 
6524
  }
6525
+ if ( typeof options === "string" ) {
6526
+ easing = options;
 
6527
  }
6528
+ // fall back from options to animation in case of partial down settings
6529
+ easing = easing || options.easing || animate.easing;
6530
+ duration = duration || options.duration || animate.duration;
6531
 
6532
+ if ( !toHide.length ) {
6533
+ return toShow.animate( showProps, duration, easing, complete );
6534
+ }
6535
+ if ( !toShow.length ) {
6536
+ return toHide.animate( hideProps, duration, easing, complete );
 
 
 
 
6537
  }
6538
 
6539
+ total = toShow.show().outerHeight();
6540
+ toHide.animate( hideProps, {
6541
+ duration: duration,
6542
+ easing: easing,
6543
+ step: function( now, fx ) {
6544
+ fx.now = Math.round( now );
6545
+ }
6546
+ });
6547
+ toShow
6548
+ .hide()
6549
+ .animate( showProps, {
6550
+ duration: duration,
6551
+ easing: easing,
6552
+ complete: complete,
6553
+ step: function( now, fx ) {
6554
+ fx.now = Math.round( now );
6555
+ if ( fx.prop !== "height" ) {
6556
+ adjust += fx.now;
6557
+ } else if ( that.options.heightStyle !== "content" ) {
6558
+ fx.now = Math.round( total - toHide.outerHeight() - adjust );
6559
+ adjust = 0;
6560
  }
6561
  }
6562
  });
 
 
 
 
6563
  },
6564
 
6565
+ _toggleComplete: function( data ) {
6566
+ var toHide = data.oldPanel;
 
6567
 
6568
+ toHide
6569
+ .removeClass( "ui-accordion-content-active" )
6570
+ .prev()
6571
+ .removeClass( "ui-corner-top" )
6572
+ .addClass( "ui-corner-all" );
 
6573
 
6574
+ // Work around for rendering bug in IE (#5421)
6575
+ if ( toHide.length ) {
6576
+ toHide.parent()[0].className = toHide.parent()[0].className;
6577
  }
6578
 
6579
+ this._trigger( "activate", null, data );
6580
+ }
6581
+ });
 
 
 
 
6582
 
6583
+ })( jQuery );
 
 
 
 
 
 
6584
 
6585
+ (function( $, undefined ) {
 
6586
 
6587
+ // used to prevent race conditions with remote data sources
6588
+ var requestIndex = 0;
6589
+
6590
+ $.widget( "ui.autocomplete", {
6591
+ version: "1.10.2",
6592
+ defaultElement: "<input>",
6593
+ options: {
6594
+ appendTo: null,
6595
+ autoFocus: false,
6596
+ delay: 300,
6597
+ minLength: 1,
6598
+ position: {
6599
+ my: "left top",
6600
+ at: "left bottom",
6601
+ collision: "none"
6602
+ },
6603
+ source: null,
6604
 
6605
+ // callbacks
6606
+ change: null,
6607
+ close: null,
6608
+ focus: null,
6609
+ open: null,
6610
+ response: null,
6611
+ search: null,
6612
+ select: null
6613
+ },
6614
 
6615
+ pending: 0,
 
 
 
 
6616
 
6617
+ _create: function() {
6618
+ // Some browsers only repeat keydown events, not keypress events,
6619
+ // so we use the suppressKeyPress flag to determine if we've already
6620
+ // handled the keydown event. #7269
6621
+ // Unfortunately the code for & in keypress is the same as the up arrow,
6622
+ // so we use the suppressKeyPressRepeat flag to avoid handling keypress
6623
+ // events when we know the keydown event was used to modify the
6624
+ // search term. #7799
6625
+ var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
6626
+ nodeName = this.element[0].nodeName.toLowerCase(),
6627
+ isTextarea = nodeName === "textarea",
6628
+ isInput = nodeName === "input";
6629
+
6630
+ this.isMultiLine =
6631
+ // Textareas are always multi-line
6632
+ isTextarea ? true :
6633
+ // Inputs are always single-line, even if inside a contentEditable element
6634
+ // IE also treats inputs as contentEditable
6635
+ isInput ? false :
6636
+ // All other element types are determined by whether or not they're contentEditable
6637
+ this.element.prop( "isContentEditable" );
6638
+
6639
+ this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
6640
+ this.isNewMenu = true;
6641
+
6642
+ this.element
6643
+ .addClass( "ui-autocomplete-input" )
6644
+ .attr( "autocomplete", "off" );
6645
+
6646
+ this._on( this.element, {
6647
+ keydown: function( event ) {
6648
+ /*jshint maxcomplexity:15*/
6649
+ if ( this.element.prop( "readOnly" ) ) {
6650
+ suppressKeyPress = true;
6651
+ suppressInput = true;
6652
+ suppressKeyPressRepeat = true;
6653
  return;
6654
  }
6655
 
6656
+ suppressKeyPress = false;
6657
+ suppressInput = false;
6658
+ suppressKeyPressRepeat = false;
6659
+ var keyCode = $.ui.keyCode;
6660
+ switch( event.keyCode ) {
6661
+ case keyCode.PAGE_UP:
6662
+ suppressKeyPress = true;
6663
+ this._move( "previousPage", event );
6664
+ break;
6665
+ case keyCode.PAGE_DOWN:
6666
+ suppressKeyPress = true;
6667
+ this._move( "nextPage", event );
6668
+ break;
6669
+ case keyCode.UP:
6670
+ suppressKeyPress = true;
6671
+ this._keyEvent( "previous", event );
6672
+ break;
6673
+ case keyCode.DOWN:
6674
+ suppressKeyPress = true;
6675
+ this._keyEvent( "next", event );
6676
+ break;
6677
+ case keyCode.ENTER:
6678
+ case keyCode.NUMPAD_ENTER:
6679
+ // when menu is open and has focus
6680
+ if ( this.menu.active ) {
6681
+ // #6055 - Opera still allows the keypress to occur
6682
+ // which causes forms to submit
6683
+ suppressKeyPress = true;
6684
+ event.preventDefault();
6685
+ this.menu.select( event );
6686
+ }
6687
+ break;
6688
+ case keyCode.TAB:
6689
+ if ( this.menu.active ) {
6690
+ this.menu.select( event );
6691
+ }
6692
+ break;
6693
+ case keyCode.ESCAPE:
6694
+ if ( this.menu.element.is( ":visible" ) ) {
6695
+ this._value( this.term );
6696
+ this.close( event );
6697
+ // Different browsers have different default behavior for escape
6698
+ // Single press can mean undo or clear
6699
+ // Double press in IE means clear the whole form
6700
+ event.preventDefault();
6701
+ }
6702
+ break;
6703
+ default:
6704
+ suppressKeyPressRepeat = true;
6705
+ // search timeout should be triggered before the input value is changed
6706
+ this._searchTimeout( event );
6707
+ break;
6708
+ }
6709
+ },
6710
+ keypress: function( event ) {
6711
+ if ( suppressKeyPress ) {
6712
+ suppressKeyPress = false;
6713
+ event.preventDefault();
6714
+ return;
6715
+ }
6716
+ if ( suppressKeyPressRepeat ) {
6717
+ return;
6718
  }
 
 
6719
 
6720
+ // replicate some key handlers to allow them to repeat in Firefox and Opera
6721
+ var keyCode = $.ui.keyCode;
6722
+ switch( event.keyCode ) {
6723
+ case keyCode.PAGE_UP:
6724
+ this._move( "previousPage", event );
6725
+ break;
6726
+ case keyCode.PAGE_DOWN:
6727
+ this._move( "nextPage", event );
6728
+ break;
6729
+ case keyCode.UP:
6730
+ this._keyEvent( "previous", event );
6731
+ break;
6732
+ case keyCode.DOWN:
6733
+ this._keyEvent( "next", event );
6734
+ break;
6735
+ }
6736
+ },
6737
+ input: function( event ) {
6738
+ if ( suppressInput ) {
6739
+ suppressInput = false;
6740
+ event.preventDefault();
6741
+ return;
6742
+ }
6743
+ this._searchTimeout( event );
6744
+ },
6745
+ focus: function() {
6746
+ this.selectedItem = null;
6747
+ this.previous = this._value();
6748
+ },
6749
+ blur: function( event ) {
6750
+ if ( this.cancelBlur ) {
6751
+ delete this.cancelBlur;
6752
+ return;
6753
+ }
6754
 
6755
+ clearTimeout( this.searching );
6756
+ this.close( event );
6757
+ this._change( event );
6758
+ }
6759
+ });
6760
 
6761
+ this._initSource();
6762
+ this.menu = $( "<ul>" )
6763
+ .addClass( "ui-autocomplete ui-front" )
6764
+ .appendTo( this._appendTo() )
6765
+ .menu({
6766
+ // custom key handling for now
6767
+ input: $(),
6768
+ // disable ARIA support, the live region takes care of that
6769
+ role: null
6770
+ })
6771
+ .hide()
6772
+ .data( "ui-menu" );
6773
 
6774
+ this._on( this.menu.element, {
6775
+ mousedown: function( event ) {
6776
+ // prevent moving focus out of the text field
6777
+ event.preventDefault();
 
 
 
 
 
 
 
 
6778
 
6779
+ // IE doesn't prevent moving focus even with event.preventDefault()
6780
+ // so we set a flag to know when we should ignore the blur event
6781
+ this.cancelBlur = true;
6782
+ this._delay(function() {
6783
+ delete this.cancelBlur;
6784
+ });
6785
 
6786
+ // clicking on the scrollbar causes focus to shift to the body
6787
+ // but we can't detect a mouseup or a click immediately afterward
6788
+ // so we have to track the next mousedown and close the menu if
6789
+ // the user clicks somewhere outside of the autocomplete
6790
+ var menuElement = this.menu.element[ 0 ];
6791
+ if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
6792
+ this._delay(function() {
6793
+ var that = this;
6794
+ this.document.one( "mousedown", function( event ) {
6795
+ if ( event.target !== that.element[ 0 ] &&
6796
+ event.target !== menuElement &&
6797
+ !$.contains( menuElement, event.target ) ) {
6798
+ that.close();
6799
+ }
6800
+ });
6801
+ });
6802
+ }
6803
+ },
6804
+ menufocus: function( event, ui ) {
6805
+ // support: Firefox
6806
+ // Prevent accidental activation of menu items in Firefox (#7024 #9118)
6807
+ if ( this.isNewMenu ) {
6808
+ this.isNewMenu = false;
6809
+ if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
6810
+ this.menu.blur();
6811
+
6812
+ this.document.one( "mousemove", function() {
6813
+ $( event.target ).trigger( event.originalEvent );
6814
+ });
6815
+
6816
+ return;
6817
  }
 
 
 
6818
  }
 
 
 
 
6819
 
6820
+ var item = ui.item.data( "ui-autocomplete-item" );
6821
+ if ( false !== this._trigger( "focus", event, { item: item } ) ) {
6822
+ // use value to match what will end up in the input, if it was a key event
6823
+ if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
6824
+ this._value( item.value );
6825
+ }
6826
+ } else {
6827
+ // Normally the input is populated with the item's value as the
6828
+ // menu is navigated, causing screen readers to notice a change and
6829
+ // announce the item. Since the focus event was canceled, this doesn't
6830
+ // happen, so we update the live region so that screen readers can
6831
+ // still notice the change and announce it.
6832
+ this.liveRegion.text( item.value );
6833
+ }
6834
+ },
6835
+ menuselect: function( event, ui ) {
6836
+ var item = ui.item.data( "ui-autocomplete-item" ),
6837
+ previous = this.previous;
6838
+
6839
+ // only trigger when focus was lost (click on menu)
6840
+ if ( this.element[0] !== this.document[0].activeElement ) {
6841
+ this.element.focus();
6842
+ this.previous = previous;
6843
+ // #6109 - IE triggers two focus events and the second
6844
+ // is asynchronous, so we need to reset the previous
6845
+ // term synchronously and asynchronously :-(
6846
+ this._delay(function() {
6847
+ this.previous = previous;
6848
+ this.selectedItem = item;
6849
+ });
6850
+ }
6851
 
6852
+ if ( false !== this._trigger( "select", event, { item: item } ) ) {
6853
+ this._value( item.value );
6854
+ }
6855
+ // reset the term after the select event
6856
+ // this allows custom select handling to work properly
6857
+ this.term = this._value();
6858
 
6859
+ this.close( event );
6860
+ this.selectedItem = item;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6861
  }
6862
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
6863
 
6864
+ this.liveRegion = $( "<span>", {
6865
+ role: "status",
6866
+ "aria-live": "polite"
6867
+ })
6868
+ .addClass( "ui-helper-hidden-accessible" )
6869
+ .insertAfter( this.element );
 
 
6870
 
6871
+ // turning off autocomplete prevents the browser from remembering the
6872
+ // value when navigating through history, so we re-enable autocomplete
6873
+ // if the page is unloaded before the widget is destroyed. #7790
6874
+ this._on( this.window, {
6875
+ beforeunload: function() {
6876
+ this.element.removeAttr( "autocomplete" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6877
  }
6878
+ });
 
 
6879
  },
6880
 
6881
+ _destroy: function() {
6882
+ clearTimeout( this.searching );
6883
+ this.element
6884
+ .removeClass( "ui-autocomplete-input" )
6885
+ .removeAttr( "autocomplete" );
6886
+ this.menu.element.remove();
6887
+ this.liveRegion.remove();
 
6888
  },
6889
 
6890
+ _setOption: function( key, value ) {
6891
+ this._super( key, value );
6892
+ if ( key === "source" ) {
6893
+ this._initSource();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6894
  }
6895
+ if ( key === "appendTo" ) {
6896
+ this.menu.element.appendTo( this._appendTo() );
 
 
 
6897
  }
6898
+ if ( key === "disabled" && value && this.xhr ) {
6899
+ this.xhr.abort();
 
 
 
 
6900
  }
6901
  },
6902
 
6903
+ _appendTo: function() {
6904
+ var element = this.options.appendTo;
 
 
 
 
 
 
 
 
 
 
 
 
 
6905
 
6906
+ if ( element ) {
6907
+ element = element.jquery || element.nodeType ?
6908
+ $( element ) :
6909
+ this.document.find( element ).eq( 0 );
6910
  }
6911
+
6912
+ if ( !element ) {
6913
+ element = this.element.closest( ".ui-front" );
6914
  }
 
6915
 
6916
+ if ( !element.length ) {
6917
+ element = this.document[0].body;
6918
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6919
 
6920
+ return element;
6921
+ },
 
 
6922
 
6923
+ _initSource: function() {
6924
+ var array, url,
6925
+ that = this;
6926
+ if ( $.isArray(this.options.source) ) {
6927
+ array = this.options.source;
6928
+ this.source = function( request, response ) {
6929
+ response( $.ui.autocomplete.filter( array, request.term ) );
6930
+ };
6931
+ } else if ( typeof this.options.source === "string" ) {
6932
+ url = this.options.source;
6933
+ this.source = function( request, response ) {
6934
+ if ( that.xhr ) {
6935
+ that.xhr.abort();
6936
  }
6937
+ that.xhr = $.ajax({
6938
+ url: url,
6939
+ data: request,
6940
+ dataType: "json",
6941
+ success: function( data ) {
6942
+ response( data );
6943
+ },
6944
+ error: function() {
6945
+ response( [] );
6946
+ }
6947
+ });
6948
+ };
6949
+ } else {
6950
+ this.source = this.options.source;
6951
  }
 
 
6952
  },
6953
 
6954
+ _searchTimeout: function( event ) {
6955
+ clearTimeout( this.searching );
6956
+ this.searching = this._delay(function() {
6957
+ // only search if the value has changed
6958
+ if ( this.term !== this._value() ) {
6959
+ this.selectedItem = null;
6960
+ this.search( null, event );
6961
+ }
6962
+ }, this.options.delay );
6963
+ },
6964
 
6965
+ search: function( value, event ) {
6966
+ value = value != null ? value : this._value();
 
 
 
 
6967
 
6968
+ // always save the actual value, not the one passed as an argument
6969
+ this.term = this._value();
 
6970
 
6971
+ if ( value.length < this.options.minLength ) {
6972
+ return this.close( event );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6973
  }
6974
 
6975
+ if ( this._trigger( "search", event ) === false ) {
6976
+ return;
6977
  }
 
 
6978
 
6979
+ return this._search( value );
6980
+ },
6981
 
6982
+ _search: function( value ) {
6983
+ this.pending++;
6984
+ this.element.addClass( "ui-autocomplete-loading" );
6985
+ this.cancelSearch = false;
6986
 
6987
+ this.source( { term: value }, this._response() );
 
 
 
 
 
 
6988
  },
6989
 
6990
+ _response: function() {
6991
+ var that = this,
6992
+ index = ++requestIndex;
 
6993
 
6994
+ return function( content ) {
6995
+ if ( index === requestIndex ) {
6996
+ that.__response( content );
6997
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6998
 
6999
+ that.pending--;
7000
+ if ( !that.pending ) {
7001
+ that.element.removeClass( "ui-autocomplete-loading" );
7002
+ }
7003
+ };
7004
+ },
 
 
 
7005
 
7006
+ __response: function( content ) {
7007
+ if ( content ) {
7008
+ content = this._normalize( content );
7009
  }
7010
+ this._trigger( "response", null, { content: content } );
7011
+ if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
7012
+ this._suggest( content );
7013
+ this._trigger( "open" );
7014
+ } else {
7015
+ // use ._close() instead of .close() so we don't cancel future searches
7016
+ this._close();
 
 
 
7017
  }
7018
+ },
7019
 
7020
+ close: function( event ) {
7021
+ this.cancelSearch = true;
7022
+ this._close( event );
7023
  },
7024
 
7025
+ _close: function( event ) {
7026
+ if ( this.menu.element.is( ":visible" ) ) {
7027
+ this.menu.element.hide();
7028
+ this.menu.blur();
7029
+ this.isNewMenu = true;
7030
+ this._trigger( "close", event );
7031
  }
7032
+ },
7033
 
7034
+ _change: function( event ) {
7035
+ if ( this.previous !== this._value() ) {
7036
+ this._trigger( "change", event, { item: this.selectedItem } );
7037
  }
7038
+ },
7039
 
7040
+ _normalize: function( items ) {
7041
+ // assume all items have the right format when the first item is complete
7042
+ if ( items.length && items[0].label && items[0].value ) {
7043
+ return items;
7044
+ }
7045
+ return $.map( items, function( item ) {
7046
+ if ( typeof item === "string" ) {
7047
+ return {
7048
+ label: item,
7049
+ value: item
7050
+ };
7051
+ }
7052
+ return $.extend({
7053
+ label: item.label || item.value,
7054
+ value: item.value || item.label
7055
+ }, item );
7056
  });
 
7057
  },
7058
 
7059
+ _suggest: function( items ) {
7060
+ var ul = this.menu.element.empty();
7061
+ this._renderMenu( ul, items );
7062
+ this.isNewMenu = true;
7063
+ this.menu.refresh();
 
 
 
 
 
 
 
 
7064
 
7065
+ // size and position menu
7066
+ ul.show();
7067
+ this._resizeMenu();
7068
+ ul.position( $.extend({
7069
+ of: this.element
7070
+ }, this.options.position ));
7071
+
7072
+ if ( this.options.autoFocus ) {
7073
+ this.menu.next();
7074
  }
7075
  },
7076
 
7077
+ _resizeMenu: function() {
7078
+ var ul = this.menu.element;
7079
+ ul.outerWidth( Math.max(
7080
+ // Firefox wraps long text (possibly a rounding bug)
7081
+ // so we add 1px to avoid the wrapping (#7513)
7082
+ ul.width( "" ).outerWidth() + 1,
7083
+ this.element.outerWidth()
7084
+ ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7085
  },
7086
 
7087
+ _renderMenu: function( ul, items ) {
7088
+ var that = this;
7089
+ $.each( items, function( index, item ) {
7090
+ that._renderItemData( ul, item );
 
 
 
 
 
 
 
 
7091
  });
7092
+ },
7093
 
7094
+ _renderItemData: function( ul, item ) {
7095
+ return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
7096
+ },
7097
+
7098
+ _renderItem: function( ul, item ) {
7099
+ return $( "<li>" )
7100
+ .append( $( "<a>" ).text( item.label ) )
7101
+ .appendTo( ul );
7102
+ },
7103
+
7104
+ _move: function( direction, event ) {
7105
+ if ( !this.menu.element.is( ":visible" ) ) {
7106
+ this.search( null, event );
7107
+ return;
7108
+ }
7109
+ if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
7110
+ this.menu.isLastItem() && /^next/.test( direction ) ) {
7111
+ this._value( this.term );
7112
+ this.menu.blur();
7113
+ return;
7114
+ }
7115
+ this.menu[ direction ]( event );
7116
+ },
7117
+
7118
+ widget: function() {
7119
+ return this.menu.element;
7120
+ },
7121
+
7122
+ _value: function() {
7123
+ return this.valueMethod.apply( this.element, arguments );
7124
+ },
7125
+
7126
+ _keyEvent: function( keyEvent, event ) {
7127
+ if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
7128
+ this._move( keyEvent, event );
7129
+
7130
+ // prevents moving cursor to beginning/end of the text field in some browsers
7131
+ event.preventDefault();
7132
+ }
7133
  }
7134
  });
7135
 
7136
+ $.extend( $.ui.autocomplete, {
7137
+ escapeRegex: function( value ) {
7138
+ return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
7139
+ },
7140
+ filter: function(array, term) {
7141
+ var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
7142
+ return $.grep( array, function(value) {
7143
+ return matcher.test( value.label || value.value || value );
7144
+ });
7145
  }
7146
  });
7147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7148
 
7149
+ // live region extension, adding a `messages` option
7150
+ // NOTE: This is an experimental API. We are still investigating
7151
+ // a full solution for string manipulation and internationalization.
7152
+ $.widget( "ui.autocomplete", $.ui.autocomplete, {
7153
  options: {
7154
+ messages: {
7155
+ noResults: "No search results.",
7156
+ results: function( amount ) {
7157
+ return amount + ( amount > 1 ? " results are" : " result is" ) +
7158
+ " available, use up and down arrow keys to navigate.";
7159
+ }
7160
+ }
 
 
7161
  },
7162
 
7163
+ __response: function( content ) {
7164
+ var message;
7165
+ this._superApply( arguments );
7166
+ if ( this.options.disabled || this.cancelSearch ) {
7167
+ return;
7168
+ }
7169
+ if ( content && content.length ) {
7170
+ message = this.options.messages.results( content.length );
7171
+ } else {
7172
+ message = this.options.messages.noResults;
7173
+ }
7174
+ this.liveRegion.text( message );
7175
+ }
7176
+ });
7177
 
7178
+ }( jQuery ));
 
 
 
 
 
 
7179
 
7180
+ (function( $, undefined ) {
7181
 
7182
+ var lastActive, startXPos, startYPos, clickDragged,
7183
+ baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
7184
+ stateClasses = "ui-state-hover ui-state-active ",
7185
+ 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",
7186
+ formResetHandler = function() {
7187
+ var buttons = $( this ).find( ":ui-button" );
7188
+ setTimeout(function() {
7189
+ buttons.button( "refresh" );
7190
+ }, 1 );
7191
+ },
7192
+ radioGroup = function( radio ) {
7193
+ var name = radio.name,
7194
+ form = radio.form,
7195
+ radios = $( [] );
7196
+ if ( name ) {
7197
+ name = name.replace( /'/g, "\\'" );
7198
+ if ( form ) {
7199
+ radios = $( form ).find( "[name='" + name + "']" );
7200
+ } else {
7201
+ radios = $( "[name='" + name + "']", radio.ownerDocument )
7202
+ .filter(function() {
7203
+ return !this.form;
7204
+ });
7205
  }
7206
+ }
7207
+ return radios;
7208
+ };
7209
 
7210
+ $.widget( "ui.button", {
7211
+ version: "1.10.2",
7212
+ defaultElement: "<button>",
7213
+ options: {
7214
+ disabled: null,
7215
+ text: true,
7216
+ label: null,
7217
+ icons: {
7218
+ primary: null,
7219
+ secondary: null
7220
  }
7221
+ },
7222
+ _create: function() {
7223
+ this.element.closest( "form" )
7224
+ .unbind( "reset" + this.eventNamespace )
7225
+ .bind( "reset" + this.eventNamespace, formResetHandler );
7226
 
7227
+ if ( typeof this.options.disabled !== "boolean" ) {
7228
+ this.options.disabled = !!this.element.prop( "disabled" );
7229
+ } else {
7230
+ this.element.prop( "disabled", this.options.disabled );
7231
  }
7232
 
7233
+ this._determineButtonType();
7234
+ this.hasTitle = !!this.buttonElement.attr( "title" );
7235
 
7236
+ var that = this,
7237
+ options = this.options,
7238
+ toggleButton = this.type === "checkbox" || this.type === "radio",
7239
+ activeClass = !toggleButton ? "ui-state-active" : "",
7240
+ focusClass = "ui-state-focus";
7241
 
7242
+ if ( options.label === null ) {
7243
+ options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
7244
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7245
 
7246
+ this._hoverable( this.buttonElement );
 
 
7247
 
7248
+ this.buttonElement
7249
+ .addClass( baseClasses )
7250
+ .attr( "role", "button" )
7251
+ .bind( "mouseenter" + this.eventNamespace, function() {
7252
+ if ( options.disabled ) {
 
 
 
 
 
7253
  return;
7254
  }
7255
+ if ( this === lastActive ) {
7256
+ $( this ).addClass( "ui-state-active" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7257
  }
7258
+ })
7259
+ .bind( "mouseleave" + this.eventNamespace, function() {
7260
+ if ( options.disabled ) {
7261
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7262
  }
7263
+ $( this ).removeClass( activeClass );
 
 
 
 
7264
  })
7265
+ .bind( "click" + this.eventNamespace, function( event ) {
7266
+ if ( options.disabled ) {
7267
+ event.preventDefault();
7268
+ event.stopImmediatePropagation();
 
 
 
 
7269
  }
 
7270
  });
7271
 
 
 
 
 
 
 
 
 
 
7272
  this.element
7273
+ .bind( "focus" + this.eventNamespace, function() {
7274
+ // no need to check disabled, focus won't be triggered anyway
7275
+ that.buttonElement.addClass( focusClass );
7276
+ })
7277
+ .bind( "blur" + this.eventNamespace, function() {
7278
+ that.buttonElement.removeClass( focusClass );
7279
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7280
 
7281
+ if ( toggleButton ) {
7282
+ this.element.bind( "change" + this.eventNamespace, function() {
7283
+ if ( clickDragged ) {
7284
+ return;
7285
+ }
7286
+ that.refresh();
7287
+ });
7288
+ // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
7289
+ // prevents issue where button state changes but checkbox/radio checked state
7290
+ // does not in Firefox (see ticket #6970)
7291
+ this.buttonElement
7292
+ .bind( "mousedown" + this.eventNamespace, function( event ) {
7293
+ if ( options.disabled ) {
7294
+ return;
7295
+ }
7296
+ clickDragged = false;
7297
+ startXPos = event.pageX;
7298
+ startYPos = event.pageY;
7299
+ })
7300
+ .bind( "mouseup" + this.eventNamespace, function( event ) {
7301
+ if ( options.disabled ) {
7302
+ return;
7303
+ }
7304
+ if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
7305
+ clickDragged = true;
7306
+ }
7307
+ });
7308
  }
7309
 
7310
+ if ( this.type === "checkbox" ) {
7311
+ this.buttonElement.bind( "click" + this.eventNamespace, function() {
7312
+ if ( options.disabled || clickDragged ) {
7313
+ return false;
7314
+ }
7315
+ });
7316
+ } else if ( this.type === "radio" ) {
7317
+ this.buttonElement.bind( "click" + this.eventNamespace, function() {
7318
+ if ( options.disabled || clickDragged ) {
7319
+ return false;
7320
+ }
7321
+ $( this ).addClass( "ui-state-active" );
7322
+ that.buttonElement.attr( "aria-pressed", "true" );
 
 
 
 
 
 
 
 
 
 
 
 
 
7323
 
7324
+ var radio = that.element[ 0 ];
7325
+ radioGroup( radio )
7326
+ .not( radio )
7327
+ .map(function() {
7328
+ return $( this ).button( "widget" )[ 0 ];
7329
+ })
7330
+ .removeClass( "ui-state-active" )
7331
+ .attr( "aria-pressed", "false" );
7332
+ });
7333
+ } else {
7334
+ this.buttonElement
7335
+ .bind( "mousedown" + this.eventNamespace, function() {
7336
+ if ( options.disabled ) {
7337
+ return false;
7338
+ }
7339
+ $( this ).addClass( "ui-state-active" );
7340
+ lastActive = this;
7341
+ that.document.one( "mouseup", function() {
7342
+ lastActive = null;
7343
+ });
7344
+ })
7345
+ .bind( "mouseup" + this.eventNamespace, function() {
7346
+ if ( options.disabled ) {
7347
+ return false;
7348
+ }
7349
+ $( this ).removeClass( "ui-state-active" );
7350
+ })
7351
+ .bind( "keydown" + this.eventNamespace, function(event) {
7352
+ if ( options.disabled ) {
7353
+ return false;
7354
+ }
7355
+ if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
7356
+ $( this ).addClass( "ui-state-active" );
7357
+ }
7358
+ })
7359
+ // see #8559, we bind to blur here in case the button element loses
7360
+ // focus between keydown and keyup, it would be left in an "active" state
7361
+ .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
7362
+ $( this ).removeClass( "ui-state-active" );
7363
+ });
7364
 
7365
+ if ( this.buttonElement.is("a") ) {
7366
+ this.buttonElement.keyup(function(event) {
7367
+ if ( event.keyCode === $.ui.keyCode.SPACE ) {
7368
+ // TODO pass through original event correctly (just as 2nd argument doesn't work)
7369
+ $( this ).click();
7370
+ }
7371
+ });
7372
+ }
7373
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
7374
 
7375
+ // TODO: pull out $.Widget's handling for the disabled option into
7376
+ // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
7377
+ // be overridden by individual plugins
7378
+ this._setOption( "disabled", options.disabled );
7379
+ this._resetButton();
7380
  },
7381
 
7382
+ _determineButtonType: function() {
7383
+ var ancestor, labelSelector, checked;
 
7384
 
7385
+ if ( this.element.is("[type=checkbox]") ) {
7386
+ this.type = "checkbox";
7387
+ } else if ( this.element.is("[type=radio]") ) {
7388
+ this.type = "radio";
7389
+ } else if ( this.element.is("input") ) {
7390
+ this.type = "input";
7391
+ } else {
7392
+ this.type = "button";
7393
+ }
7394
 
7395
+ if ( this.type === "checkbox" || this.type === "radio" ) {
7396
+ // we don't search against the document in case the element
7397
+ // is disconnected from the DOM
7398
+ ancestor = this.element.parents().last();
7399
+ labelSelector = "label[for='" + this.element.attr("id") + "']";
7400
+ this.buttonElement = ancestor.find( labelSelector );
7401
+ if ( !this.buttonElement.length ) {
7402
+ ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
7403
+ this.buttonElement = ancestor.filter( labelSelector );
7404
+ if ( !this.buttonElement.length ) {
7405
+ this.buttonElement = ancestor.find( labelSelector );
7406
+ }
7407
+ }
7408
+ this.element.addClass( "ui-helper-hidden-accessible" );
7409
 
7410
+ checked = this.element.is( ":checked" );
7411
+ if ( checked ) {
7412
+ this.buttonElement.addClass( "ui-state-active" );
7413
+ }
7414
+ this.buttonElement.prop( "aria-pressed", checked );
7415
+ } else {
7416
+ this.buttonElement = this.element;
7417
+ }
7418
  },
7419
+
7420
+ widget: function() {
7421
+ return this.buttonElement;
7422
  },
7423
 
7424
+ _destroy: function() {
7425
+ this.element
7426
+ .removeClass( "ui-helper-hidden-accessible" );
7427
+ this.buttonElement
7428
+ .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
7429
+ .removeAttr( "role" )
7430
+ .removeAttr( "aria-pressed" )
7431
+ .html( this.buttonElement.find(".ui-button-text").html() );
7432
 
7433
+ if ( !this.hasTitle ) {
7434
+ this.buttonElement.removeAttr( "title" );
 
 
 
 
7435
  }
7436
+ },
7437
 
7438
+ _setOption: function( key, value ) {
7439
+ this._super( key, value );
7440
+ if ( key === "disabled" ) {
7441
+ if ( value ) {
7442
+ this.element.prop( "disabled", true );
7443
+ } else {
7444
+ this.element.prop( "disabled", false );
7445
+ }
7446
+ return;
7447
  }
7448
+ this._resetButton();
7449
+ },
7450
 
7451
+ refresh: function() {
7452
+ //See #8237 & #8828
7453
+ var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
7454
 
7455
+ if ( isDisabled !== this.options.disabled ) {
7456
+ this._setOption( "disabled", isDisabled );
7457
+ }
7458
+ if ( this.type === "radio" ) {
7459
+ radioGroup( this.element[0] ).each(function() {
7460
+ if ( $( this ).is( ":checked" ) ) {
7461
+ $( this ).button( "widget" )
7462
+ .addClass( "ui-state-active" )
7463
+ .attr( "aria-pressed", "true" );
7464
+ } else {
7465
+ $( this ).button( "widget" )
7466
+ .removeClass( "ui-state-active" )
7467
+ .attr( "aria-pressed", "false" );
7468
+ }
7469
+ });
7470
+ } else if ( this.type === "checkbox" ) {
7471
+ if ( this.element.is( ":checked" ) ) {
7472
+ this.buttonElement
7473
+ .addClass( "ui-state-active" )
7474
+ .attr( "aria-pressed", "true" );
7475
+ } else {
7476
+ this.buttonElement
7477
+ .removeClass( "ui-state-active" )
7478
+ .attr( "aria-pressed", "false" );
7479
+ }
7480
+ }
7481
  },
7482
 
7483
+ _resetButton: function() {
7484
+ if ( this.type === "input" ) {
7485
+ if ( this.options.label ) {
7486
+ this.element.val( this.options.label );
7487
+ }
7488
+ return;
 
 
7489
  }
7490
+ var buttonElement = this.buttonElement.removeClass( typeClasses ),
7491
+ buttonText = $( "<span></span>", this.document[0] )
7492
+ .addClass( "ui-button-text" )
7493
+ .html( this.options.label )
7494
+ .appendTo( buttonElement.empty() )
7495
+ .text(),
7496
+ icons = this.options.icons,
7497
+ multipleIcons = icons.primary && icons.secondary,
7498
+ buttonClasses = [];
7499
 
7500
+ if ( icons.primary || icons.secondary ) {
7501
+ if ( this.options.text ) {
7502
+ buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
7503
+ }
7504
 
7505
+ if ( icons.primary ) {
7506
+ buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
7507
+ }
7508
 
7509
+ if ( icons.secondary ) {
7510
+ buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
 
 
7511
  }
7512
 
7513
+ if ( !this.options.text ) {
7514
+ buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
7515
+
7516
+ if ( !this.hasTitle ) {
7517
+ buttonElement.attr( "title", $.trim( buttonText ) );
 
 
 
 
 
 
 
7518
  }
7519
  }
7520
  } else {
7521
+ buttonClasses.push( "ui-button-text-only" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7522
  }
7523
+ buttonElement.addClass( buttonClasses.join( " " ) );
7524
+ }
7525
+ });
7526
 
7527
+ $.widget( "ui.buttonset", {
7528
+ version: "1.10.2",
7529
+ options: {
7530
+ items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
7531
  },
7532
 
7533
+ _create: function() {
7534
+ this.element.addClass( "ui-buttonset" );
7535
+ },
 
 
 
 
 
 
 
7536
 
7537
+ _init: function() {
7538
+ this.refresh();
7539
  },
7540
 
7541
+ _setOption: function( key, value ) {
7542
+ if ( key === "disabled" ) {
7543
+ this.buttons.button( "option", key, value );
 
 
 
7544
  }
7545
 
7546
+ this._super( key, value );
7547
  },
7548
 
7549
+ refresh: function() {
7550
+ var rtl = this.element.css( "direction" ) === "rtl";
 
 
 
 
 
 
 
 
 
7551
 
7552
+ this.buttons = this.element.find( this.options.items )
7553
+ .filter( ":ui-button" )
7554
+ .button( "refresh" )
7555
+ .end()
7556
+ .not( ":ui-button" )
7557
+ .button()
7558
+ .end()
7559
+ .map(function() {
7560
+ return $( this ).button( "widget" )[ 0 ];
7561
+ })
7562
+ .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
7563
+ .filter( ":first" )
7564
+ .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
7565
+ .end()
7566
+ .filter( ":last" )
7567
+ .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
7568
+ .end()
7569
+ .end();
 
7570
  },
7571
 
7572
+ _destroy: function() {
7573
+ this.element.removeClass( "ui-buttonset" );
7574
+ this.buttons
7575
+ .map(function() {
7576
+ return $( this ).button( "widget" )[ 0 ];
7577
+ })
7578
+ .removeClass( "ui-corner-left ui-corner-right" )
7579
+ .end()
7580
+ .button( "destroy" );
7581
+ }
7582
+ });
7583
 
7584
+ }( jQuery ) );
 
 
7585
 
7586
+ (function( $, undefined ) {
7587
 
7588
+ $.extend($.ui, { datepicker: { version: "1.10.2" } });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7589
 
7590
+ var PROP_NAME = "datepicker",
7591
+ dpuuid = new Date().getTime(),
7592
+ instActive;
 
 
7593
 
7594
+ /* Date picker manager.
7595
+ Use the singleton instance of this class, $.datepicker, to interact with the date picker.
7596
+ Settings for (groups of) date pickers are maintained in an instance object,
7597
+ allowing multiple different settings on the same page. */
7598
 
7599
+ function Datepicker() {
7600
+ this._curInst = null; // The current instance in use
7601
+ this._keyEvent = false; // If the last event was a key event
7602
+ this._disabledInputs = []; // List of date picker inputs that have been disabled
7603
+ this._datepickerShowing = false; // True if the popup picker is showing , false if not
7604
+ this._inDialog = false; // True if showing within a "dialog", false if not
7605
+ this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
7606
+ this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
7607
+ this._appendClass = "ui-datepicker-append"; // The name of the append marker class
7608
+ this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
7609
+ this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
7610
+ this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
7611
+ this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
7612
+ this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
7613
+ this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
7614
+ this.regional = []; // Available regional settings, indexed by language code
7615
+ this.regional[""] = { // Default regional settings
7616
+ closeText: "Done", // Display text for close link
7617
+ prevText: "Prev", // Display text for previous month link
7618
+ nextText: "Next", // Display text for next month link
7619
+ currentText: "Today", // Display text for current month link
7620
+ monthNames: ["January","February","March","April","May","June",
7621
+ "July","August","September","October","November","December"], // Names of months for drop-down and formatting
7622
+ monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
7623
+ dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
7624
+ dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
7625
+ dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
7626
+ weekHeader: "Wk", // Column header for week of the year
7627
+ dateFormat: "mm/dd/yy", // See format options on parseDate
7628
+ firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
7629
+ isRTL: false, // True if right-to-left language, false if left-to-right
7630
+ showMonthAfterYear: false, // True if the year select precedes month, false for month then year
7631
+ yearSuffix: "" // Additional text to append to the year in the month headers
7632
+ };
7633
+ this._defaults = { // Global defaults for all the date picker instances
7634
+ showOn: "focus", // "focus" for popup on focus,
7635
+ // "button" for trigger button, or "both" for either
7636
+ showAnim: "fadeIn", // Name of jQuery animation for popup
7637
+ showOptions: {}, // Options for enhanced animations
7638
+ defaultDate: null, // Used when field is blank: actual date,
7639
+ // +/-number for offset from today, null for today
7640
+ appendText: "", // Display text following the input box, e.g. showing the format
7641
+ buttonText: "...", // Text for trigger button
7642
+ buttonImage: "", // URL for trigger button image
7643
+ buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
7644
+ hideIfNoPrevNext: false, // True to hide next/previous month links
7645
+ // if not applicable, false to just disable them
7646
+ navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
7647
+ gotoCurrent: false, // True if today link goes back to current selection instead
7648
+ changeMonth: false, // True if month can be selected directly, false if only prev/next
7649
+ changeYear: false, // True if year can be selected directly, false if only prev/next
7650
+ yearRange: "c-10:c+10", // Range of years to display in drop-down,
7651
+ // either relative to today's year (-nn:+nn), relative to currently displayed year
7652
+ // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
7653
+ showOtherMonths: false, // True to show dates in other months, false to leave blank
7654
+ selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
7655
+ showWeek: false, // True to show week of the year, false to not show it
7656
+ calculateWeek: this.iso8601Week, // How to calculate the week of the year,
7657
+ // takes a Date and returns the number of the week for it
7658
+ shortYearCutoff: "+10", // Short year values < this are in the current century,
7659
+ // > this are in the previous century,
7660
+ // string value starting with "+" for current year + value
7661
+ minDate: null, // The earliest selectable date, or null for no limit
7662
+ maxDate: null, // The latest selectable date, or null for no limit
7663
+ duration: "fast", // Duration of display/closure
7664
+ beforeShowDay: null, // Function that takes a date and returns an array with
7665
+ // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
7666
+ // [2] = cell title (optional), e.g. $.datepicker.noWeekends
7667
+ beforeShow: null, // Function that takes an input field and
7668
+ // returns a set of custom settings for the date picker
7669
+ onSelect: null, // Define a callback function when a date is selected
7670
+ onChangeMonthYear: null, // Define a callback function when the month or year is changed
7671
+ onClose: null, // Define a callback function when the datepicker is closed
7672
+ numberOfMonths: 1, // Number of months to show at a time
7673
+ showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
7674
+ stepMonths: 1, // Number of months to step back/forward
7675
+ stepBigMonths: 12, // Number of months to step back/forward for the big links
7676
+ altField: "", // Selector for an alternate field to store selected dates into
7677
+ altFormat: "", // The date format to use for the alternate field
7678
+ constrainInput: true, // The input is constrained by the current date format
7679
+ showButtonPanel: false, // True to show button panel, false to not show it
7680
+ autoSize: false, // True to size the input for the date format, false to leave as is
7681
+ disabled: false // The initial disabled state
7682
+ };
7683
+ $.extend(this._defaults, this.regional[""]);
7684
+ this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
7685
+ }
7686
 
7687
+ $.extend(Datepicker.prototype, {
7688
+ /* Class name added to elements to indicate already configured with a date picker. */
7689
+ markerClassName: "hasDatepicker",
7690
 
7691
+ //Keep track of the maximum number of rows displayed (see #7043)
7692
+ maxRows: 4,
 
 
 
 
 
 
7693
 
7694
+ // TODO rename to "widget" when switching to widget factory
7695
+ _widgetDatepicker: function() {
7696
+ return this.dpDiv;
7697
  },
 
 
 
 
 
 
 
 
 
 
 
 
7698
 
7699
+ /* Override the default settings for all instances of the date picker.
7700
+ * @param settings object - the new settings to use as defaults (anonymous object)
7701
+ * @return the manager object
7702
+ */
7703
+ setDefaults: function(settings) {
7704
+ extendRemove(this._defaults, settings || {});
7705
+ return this;
7706
+ },
7707
 
7708
+ /* Attach the date picker to a jQuery selection.
7709
+ * @param target element - the target input field or division or span
7710
+ * @param settings object - the new settings to use for this date picker instance (anonymous)
7711
+ */
7712
+ _attachDatepicker: function(target, settings) {
7713
+ var nodeName, inline, inst;
7714
+ nodeName = target.nodeName.toLowerCase();
7715
+ inline = (nodeName === "div" || nodeName === "span");
7716
+ if (!target.id) {
7717
+ this.uuid += 1;
7718
+ target.id = "dp" + this.uuid;
7719
+ }
7720
+ inst = this._newInst($(target), inline);
7721
+ inst.settings = $.extend({}, settings || {});
7722
+ if (nodeName === "input") {
7723
+ this._connectDatepicker(target, inst);
7724
+ } else if (inline) {
7725
+ this._inlineDatepicker(target, inst);
7726
+ }
7727
  },
7728
 
7729
+ /* Create a new instance object. */
7730
+ _newInst: function(target, inline) {
7731
+ var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
7732
+ return {id: id, input: target, // associated target
7733
+ selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
7734
+ drawMonth: 0, drawYear: 0, // month being drawn
7735
+ inline: inline, // is datepicker inline or not
7736
+ dpDiv: (!inline ? this.dpDiv : // presentation div
7737
+ bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
7738
  },
7739
 
7740
+ /* Attach the date picker to an input field. */
7741
+ _connectDatepicker: function(target, inst) {
7742
+ var input = $(target);
7743
+ inst.append = $([]);
7744
+ inst.trigger = $([]);
7745
+ if (input.hasClass(this.markerClassName)) {
7746
+ return;
7747
+ }
7748
+ this._attachments(input, inst);
7749
+ input.addClass(this.markerClassName).keydown(this._doKeyDown).
7750
+ keypress(this._doKeyPress).keyup(this._doKeyUp);
7751
+ this._autoSize(inst);
7752
+ $.data(target, PROP_NAME, inst);
7753
+ //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
7754
+ if( inst.settings.disabled ) {
7755
+ this._disableDatepicker( target );
7756
+ }
7757
  },
 
 
 
 
 
 
 
 
 
 
 
 
7758
 
7759
+ /* Make attachments based on settings. */
7760
+ _attachments: function(input, inst) {
7761
+ var showOn, buttonText, buttonImage,
7762
+ appendText = this._get(inst, "appendText"),
7763
+ isRTL = this._get(inst, "isRTL");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7764
 
7765
+ if (inst.append) {
7766
+ inst.append.remove();
7767
+ }
7768
+ if (appendText) {
7769
+ inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
7770
+ input[isRTL ? "before" : "after"](inst.append);
 
 
 
 
 
 
7771
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7772
 
7773
+ input.unbind("focus", this._showDatepicker);
 
 
7774
 
7775
+ if (inst.trigger) {
7776
+ inst.trigger.remove();
7777
+ }
7778
 
7779
+ showOn = this._get(inst, "showOn");
7780
+ if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
7781
+ input.focus(this._showDatepicker);
7782
+ }
7783
+ if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
7784
+ buttonText = this._get(inst, "buttonText");
7785
+ buttonImage = this._get(inst, "buttonImage");
7786
+ inst.trigger = $(this._get(inst, "buttonImageOnly") ?
7787
+ $("<img/>").addClass(this._triggerClass).
7788
+ attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
7789
+ $("<button type='button'></button>").addClass(this._triggerClass).
7790
+ html(!buttonImage ? buttonText : $("<img/>").attr(
7791
+ { src:buttonImage, alt:buttonText, title:buttonText })));
7792
+ input[isRTL ? "before" : "after"](inst.trigger);
7793
+ inst.trigger.click(function() {
7794
+ if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
7795
+ $.datepicker._hideDatepicker();
7796
+ } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
7797
+ $.datepicker._hideDatepicker();
7798
+ $.datepicker._showDatepicker(input[0]);
7799
+ } else {
7800
+ $.datepicker._showDatepicker(input[0]);
7801
+ }
7802
+ return false;
7803
+ });
7804
+ }
7805
  },
7806
 
7807
+ /* Apply the maximum length for the date format. */
7808
+ _autoSize: function(inst) {
7809
+ if (this._get(inst, "autoSize") && !inst.inline) {
7810
+ var findMax, max, maxI, i,
7811
+ date = new Date(2009, 12 - 1, 20), // Ensure double digits
7812
+ dateFormat = this._get(inst, "dateFormat");
7813
 
7814
+ if (dateFormat.match(/[DM]/)) {
7815
+ findMax = function(names) {
7816
+ max = 0;
7817
+ maxI = 0;
7818
+ for (i = 0; i < names.length; i++) {
7819
+ if (names[i].length > max) {
7820
+ max = names[i].length;
7821
+ maxI = i;
7822
+ }
7823
+ }
7824
+ return maxI;
7825
+ };
7826
+ date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
7827
+ "monthNames" : "monthNamesShort"))));
7828
+ date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
7829
+ "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
7830
  }
7831
+ inst.input.attr("size", this._formatDate(inst, date).length);
 
 
 
7832
  }
7833
  },
7834
 
7835
+ /* Attach an inline date picker to a div. */
7836
+ _inlineDatepicker: function(target, inst) {
7837
+ var divSpan = $(target);
7838
+ if (divSpan.hasClass(this.markerClassName)) {
7839
+ return;
7840
+ }
7841
+ divSpan.addClass(this.markerClassName).append(inst.dpDiv);
7842
+ $.data(target, PROP_NAME, inst);
7843
+ this._setDate(inst, this._getDefaultDate(inst), true);
7844
+ this._updateDatepicker(inst);
7845
+ this._updateAlternate(inst);
7846
+ //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
7847
+ if( inst.settings.disabled ) {
7848
+ this._disableDatepicker( target );
7849
+ }
7850
+ // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
7851
+ // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
7852
+ inst.dpDiv.css( "display", "block" );
7853
  },
7854
 
7855
+ /* Pop-up the date picker in a "dialog" box.
7856
+ * @param input element - ignored
7857
+ * @param date string or Date - the initial date to display
7858
+ * @param onSelect function - the function to call when a date is selected
7859
+ * @param settings object - update the dialog date picker instance's settings (anonymous object)
7860
+ * @param pos int[2] - coordinates for the dialog's position within the screen or
7861
+ * event - with x/y coordinates or
7862
+ * leave empty for default (screen centre)
7863
+ * @return the manager object
7864
+ */
7865
+ _dialogDatepicker: function(input, date, onSelect, settings, pos) {
7866
+ var id, browserWidth, browserHeight, scrollX, scrollY,
7867
+ inst = this._dialogInst; // internal instance
7868
 
7869
+ if (!inst) {
7870
+ this.uuid += 1;
7871
+ id = "dp" + this.uuid;
7872
+ this._dialogInput = $("<input type='text' id='" + id +
7873
+ "' style='position: absolute; top: -100px; width: 0px;'/>");
7874
+ this._dialogInput.keydown(this._doKeyDown);
7875
+ $("body").append(this._dialogInput);
7876
+ inst = this._dialogInst = this._newInst(this._dialogInput, false);
7877
+ inst.settings = {};
7878
+ $.data(this._dialogInput[0], PROP_NAME, inst);
7879
+ }
7880
+ extendRemove(inst.settings, settings || {});
7881
+ date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
7882
+ this._dialogInput.val(date);
7883
 
7884
+ this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
7885
+ if (!this._pos) {
7886
+ browserWidth = document.documentElement.clientWidth;
7887
+ browserHeight = document.documentElement.clientHeight;
7888
+ scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
7889
+ scrollY = document.documentElement.scrollTop || document.body.scrollTop;
7890
+ this._pos = // should use actual width/height below
7891
+ [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
7892
+ }
7893
 
7894
+ // move input on screen for focus, but hidden behind dialog
7895
+ this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
7896
+ inst.settings.onSelect = onSelect;
7897
+ this._inDialog = true;
7898
+ this.dpDiv.addClass(this._dialogClass);
7899
+ this._showDatepicker(this._dialogInput[0]);
7900
+ if ($.blockUI) {
7901
+ $.blockUI(this.dpDiv);
7902
+ }
7903
+ $.data(this._dialogInput[0], PROP_NAME, inst);
7904
+ return this;
7905
  },
7906
 
7907
+ /* Detach a datepicker from its control.
7908
+ * @param target element - the target input field or division or span
7909
+ */
7910
+ _destroyDatepicker: function(target) {
7911
+ var nodeName,
7912
+ $target = $(target),
7913
+ inst = $.data(target, PROP_NAME);
7914
 
7915
+ if (!$target.hasClass(this.markerClassName)) {
7916
+ return;
7917
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7918
 
7919
+ nodeName = target.nodeName.toLowerCase();
7920
+ $.removeData(target, PROP_NAME);
7921
+ if (nodeName === "input") {
7922
+ inst.append.remove();
7923
+ inst.trigger.remove();
7924
+ $target.removeClass(this.markerClassName).
7925
+ unbind("focus", this._showDatepicker).
7926
+ unbind("keydown", this._doKeyDown).
7927
+ unbind("keypress", this._doKeyPress).
7928
+ unbind("keyup", this._doKeyUp);
7929
+ } else if (nodeName === "div" || nodeName === "span") {
7930
+ $target.removeClass(this.markerClassName).empty();
7931
+ }
7932
+ },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7933
 
7934
+ /* Enable the date picker to a jQuery selection.
7935
+ * @param target element - the target input field or division or span
7936
+ */
7937
+ _enableDatepicker: function(target) {
7938
+ var nodeName, inline,
7939
+ $target = $(target),
7940
+ inst = $.data(target, PROP_NAME);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7941
 
7942
+ if (!$target.hasClass(this.markerClassName)) {
7943
+ return;
7944
+ }
 
7945
 
7946
+ nodeName = target.nodeName.toLowerCase();
7947
+ if (nodeName === "input") {
7948
+ target.disabled = false;
7949
+ inst.trigger.filter("button").
7950
+ each(function() { this.disabled = false; }).end().
7951
+ filter("img").css({opacity: "1.0", cursor: ""});
7952
+ } else if (nodeName === "div" || nodeName === "span") {
7953
+ inline = $target.children("." + this._inlineClass);
7954
+ inline.children().removeClass("ui-state-disabled");
7955
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7956
+ prop("disabled", false);
7957
+ }
7958
+ this._disabledInputs = $.map(this._disabledInputs,
7959
+ function(value) { return (value === target ? null : value); }); // delete entry
7960
+ },
7961
 
7962
+ /* Disable the date picker to a jQuery selection.
7963
+ * @param target element - the target input field or division or span
7964
+ */
7965
+ _disableDatepicker: function(target) {
7966
+ var nodeName, inline,
7967
+ $target = $(target),
7968
+ inst = $.data(target, PROP_NAME);
7969
 
7970
+ if (!$target.hasClass(this.markerClassName)) {
7971
+ return;
7972
+ }
 
 
 
 
7973
 
7974
+ nodeName = target.nodeName.toLowerCase();
7975
+ if (nodeName === "input") {
7976
+ target.disabled = true;
7977
+ inst.trigger.filter("button").
7978
+ each(function() { this.disabled = true; }).end().
7979
+ filter("img").css({opacity: "0.5", cursor: "default"});
7980
+ } else if (nodeName === "div" || nodeName === "span") {
7981
+ inline = $target.children("." + this._inlineClass);
7982
+ inline.children().addClass("ui-state-disabled");
7983
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7984
+ prop("disabled", true);
7985
+ }
7986
+ this._disabledInputs = $.map(this._disabledInputs,
7987
+ function(value) { return (value === target ? null : value); }); // delete entry
7988
+ this._disabledInputs[this._disabledInputs.length] = target;
7989
+ },
7990
 
7991
+ /* Is the first field in a jQuery collection disabled as a datepicker?
7992
+ * @param target element - the target input field or division or span
7993
+ * @return boolean - true if disabled, false if enabled
7994
+ */
7995
+ _isDisabledDatepicker: function(target) {
7996
+ if (!target) {
7997
+ return false;
7998
+ }
7999
+ for (var i = 0; i < this._disabledInputs.length; i++) {
8000
+ if (this._disabledInputs[i] === target) {
8001
+ return true;
8002
  }
8003
+ }
8004
+ return false;
8005
+ },
8006
 
8007
+ /* Retrieve the instance data for the target control.
8008
+ * @param target element - the target input field or division or span
8009
+ * @return object - the associated instance data
8010
+ * @throws error if a jQuery problem getting data
8011
+ */
8012
+ _getInst: function(target) {
8013
+ try {
8014
+ return $.data(target, PROP_NAME);
8015
+ }
8016
+ catch (err) {
8017
+ throw "Missing instance data for this datepicker";
8018
  }
8019
+ },
8020
 
8021
+ /* Update or retrieve the settings for a date picker attached to an input field or division.
8022
+ * @param target element - the target input field or division or span
8023
+ * @param name object - the new settings to update or
8024
+ * string - the name of the setting to change or retrieve,
8025
+ * when retrieving also "all" for all instance settings or
8026
+ * "defaults" for all global defaults
8027
+ * @param value any - the new value for the setting
8028
+ * (omit if above is an object or to retrieve a value)
8029
+ */
8030
+ _optionDatepicker: function(target, name, value) {
8031
+ var settings, date, minDate, maxDate,
8032
+ inst = this._getInst(target);
8033
 
8034
+ if (arguments.length === 2 && typeof name === "string") {
8035
+ return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
8036
+ (inst ? (name === "all" ? $.extend({}, inst.settings) :
8037
+ this._get(inst, name)) : null));
8038
  }
8039
 
8040
+ settings = name || {};
8041
+ if (typeof name === "string") {
8042
+ settings = {};
8043
+ settings[name] = value;
 
8044
  }
8045
 
8046
+ if (inst) {
8047
+ if (this._curInst === inst) {
8048
+ this._hideDatepicker();
8049
+ }
 
 
 
8050
 
8051
+ date = this._getDateDatepicker(target, true);
8052
+ minDate = this._getMinMaxDate(inst, "min");
8053
+ maxDate = this._getMinMaxDate(inst, "max");
8054
+ extendRemove(inst.settings, settings);
8055
+ // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
8056
+ if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
8057
+ inst.settings.minDate = this._formatDate(inst, minDate);
8058
+ }
8059
+ if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
8060
+ inst.settings.maxDate = this._formatDate(inst, maxDate);
8061
+ }
8062
+ if ( "disabled" in settings ) {
8063
+ if ( settings.disabled ) {
8064
+ this._disableDatepicker(target);
8065
+ } else {
8066
+ this._enableDatepicker(target);
8067
  }
8068
+ }
8069
+ this._attachments($(target), inst);
8070
+ this._autoSize(inst);
8071
+ this._setDate(inst, date);
8072
+ this._updateAlternate(inst);
8073
+ this._updateDatepicker(inst);
 
 
 
 
 
 
 
 
 
 
8074
  }
8075
+ },
8076
 
8077
+ // change method deprecated
8078
+ _changeDatepicker: function(target, name, value) {
8079
+ this._optionDatepicker(target, name, value);
8080
+ },
8081
+
8082
+ /* Redraw the date picker attached to an input field or division.
8083
+ * @param target element - the target input field or division or span
8084
+ */
8085
+ _refreshDatepicker: function(target) {
8086
+ var inst = this._getInst(target);
8087
+ if (inst) {
8088
+ this._updateDatepicker(inst);
8089
  }
8090
+ },
8091
 
8092
+ /* Set the dates for a jQuery selection.
8093
+ * @param target element - the target input field or division or span
8094
+ * @param date Date - the new date
8095
+ */
8096
+ _setDateDatepicker: function(target, date) {
8097
+ var inst = this._getInst(target);
8098
+ if (inst) {
8099
+ this._setDate(inst, date);
8100
+ this._updateDatepicker(inst);
8101
+ this._updateAlternate(inst);
8102
  }
8103
+ },
8104
 
8105
+ /* Get the date(s) for the first entry in a jQuery selection.
8106
+ * @param target element - the target input field or division or span
8107
+ * @param noDefault boolean - true if no default date is to be used
8108
+ * @return Date - the current date
8109
+ */
8110
+ _getDateDatepicker: function(target, noDefault) {
8111
+ var inst = this._getInst(target);
8112
+ if (inst && !inst.inline) {
8113
+ this._setDateFromField(inst, noDefault);
8114
+ }
8115
+ return (inst ? this._getDate(inst) : null);
8116
+ },
 
 
 
8117
 
8118
+ /* Handle keystrokes. */
8119
+ _doKeyDown: function(event) {
8120
+ var onSelect, dateStr, sel,
8121
+ inst = $.datepicker._getInst(event.target),
8122
+ handled = true,
8123
+ isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
 
 
 
 
 
 
 
 
 
8124
 
8125
+ inst._keyEvent = true;
8126
+ if ($.datepicker._datepickerShowing) {
8127
+ switch (event.keyCode) {
8128
+ case 9: $.datepicker._hideDatepicker();
8129
+ handled = false;
8130
+ break; // hide on tab out
8131
+ case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
8132
+ $.datepicker._currentClass + ")", inst.dpDiv);
8133
+ if (sel[0]) {
8134
+ $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
8135
+ }
 
 
 
 
 
 
 
 
8136
 
8137
+ onSelect = $.datepicker._get(inst, "onSelect");
8138
+ if (onSelect) {
8139
+ dateStr = $.datepicker._formatDate(inst);
8140
 
8141
+ // trigger custom callback
8142
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
8143
+ } else {
8144
+ $.datepicker._hideDatepicker();
8145
+ }
8146
 
8147
+ return false; // don't submit the form
8148
+ case 27: $.datepicker._hideDatepicker();
8149
+ break; // hide on escape
8150
+ case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8151
+ -$.datepicker._get(inst, "stepBigMonths") :
8152
+ -$.datepicker._get(inst, "stepMonths")), "M");
8153
+ break; // previous month/year on page up/+ ctrl
8154
+ case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8155
+ +$.datepicker._get(inst, "stepBigMonths") :
8156
+ +$.datepicker._get(inst, "stepMonths")), "M");
8157
+ break; // next month/year on page down/+ ctrl
8158
+ case 35: if (event.ctrlKey || event.metaKey) {
8159
+ $.datepicker._clearDate(event.target);
8160
+ }
8161
+ handled = event.ctrlKey || event.metaKey;
8162
+ break; // clear on ctrl or command +end
8163
+ case 36: if (event.ctrlKey || event.metaKey) {
8164
+ $.datepicker._gotoToday(event.target);
8165
+ }
8166
+ handled = event.ctrlKey || event.metaKey;
8167
+ break; // current on ctrl or command +home
8168
+ case 37: if (event.ctrlKey || event.metaKey) {
8169
+ $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
8170
+ }
8171
+ handled = event.ctrlKey || event.metaKey;
8172
+ // -1 day on ctrl or command +left
8173
+ if (event.originalEvent.altKey) {
8174
+ $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8175
+ -$.datepicker._get(inst, "stepBigMonths") :
8176
+ -$.datepicker._get(inst, "stepMonths")), "M");
8177
+ }
8178
+ // next month/year on alt +left on Mac
8179
+ break;
8180
+ case 38: if (event.ctrlKey || event.metaKey) {
8181
+ $.datepicker._adjustDate(event.target, -7, "D");
8182
+ }
8183
+ handled = event.ctrlKey || event.metaKey;
8184
+ break; // -1 week on ctrl or command +up
8185
+ case 39: if (event.ctrlKey || event.metaKey) {
8186
+ $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
8187
+ }
8188
+ handled = event.ctrlKey || event.metaKey;
8189
+ // +1 day on ctrl or command +right
8190
+ if (event.originalEvent.altKey) {
8191
+ $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8192
+ +$.datepicker._get(inst, "stepBigMonths") :
8193
+ +$.datepicker._get(inst, "stepMonths")), "M");
8194
+ }
8195
+ // next month/year on alt +right
8196
+ break;
8197
+ case 40: if (event.ctrlKey || event.metaKey) {
8198
+ $.datepicker._adjustDate(event.target, +7, "D");
8199
+ }
8200
+ handled = event.ctrlKey || event.metaKey;
8201
+ break; // +1 week on ctrl or command +down
8202
+ default: handled = false;
8203
+ }
8204
+ } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
8205
+ $.datepicker._showDatepicker(this);
8206
+ } else {
8207
+ handled = false;
8208
+ }
8209
 
8210
+ if (handled) {
8211
+ event.preventDefault();
8212
+ event.stopPropagation();
8213
+ }
8214
+ },
8215
 
8216
+ /* Filter entered characters - based on date format. */
8217
+ _doKeyPress: function(event) {
8218
+ var chars, chr,
8219
+ inst = $.datepicker._getInst(event.target);
8220
 
8221
+ if ($.datepicker._get(inst, "constrainInput")) {
8222
+ chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
8223
+ chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
8224
+ return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
8225
+ }
8226
+ },
8227
 
8228
+ /* Synchronise manual entry and field/alternate field. */
8229
+ _doKeyUp: function(event) {
8230
+ var date,
8231
+ inst = $.datepicker._getInst(event.target);
8232
 
8233
+ if (inst.input.val() !== inst.lastVal) {
8234
+ try {
8235
+ date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
8236
+ (inst.input ? inst.input.val() : null),
8237
+ $.datepicker._getFormatConfig(inst));
8238
 
8239
+ if (date) { // only if valid
8240
+ $.datepicker._setDateFromField(inst);
8241
+ $.datepicker._updateAlternate(inst);
8242
+ $.datepicker._updateDatepicker(inst);
8243
  }
8244
  }
8245
+ catch (err) {
 
 
8246
  }
8247
+ }
8248
+ return true;
8249
+ },
8250
 
8251
+ /* Pop-up the date picker for a given input field.
8252
+ * If false returned from beforeShow event handler do not show.
8253
+ * @param input element - the input field attached to the date picker or
8254
+ * event - if triggered by focus
8255
+ */
8256
+ _showDatepicker: function(input) {
8257
+ input = input.target || input;
8258
+ if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
8259
+ input = $("input", input.parentNode)[0];
8260
+ }
 
 
 
 
 
8261
 
8262
+ if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
8263
+ return;
8264
+ }
 
 
 
 
 
8265
 
8266
+ var inst, beforeShow, beforeShowSettings, isFixed,
8267
+ offset, showAnim, duration;
 
 
 
8268
 
8269
+ inst = $.datepicker._getInst(input);
8270
+ if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
8271
+ $.datepicker._curInst.dpDiv.stop(true, true);
8272
+ if ( inst && $.datepicker._datepickerShowing ) {
8273
+ $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
8274
+ }
8275
  }
8276
 
8277
+ beforeShow = $.datepicker._get(inst, "beforeShow");
8278
+ beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
8279
+ if(beforeShowSettings === false){
8280
+ return;
8281
+ }
8282
+ extendRemove(inst.settings, beforeShowSettings);
8283
 
8284
+ inst.lastVal = null;
8285
+ $.datepicker._lastInput = input;
8286
+ $.datepicker._setDateFromField(inst);
8287
 
8288
+ if ($.datepicker._inDialog) { // hide cursor
8289
+ input.value = "";
8290
+ }
8291
+ if (!$.datepicker._pos) { // position below input
8292
+ $.datepicker._pos = $.datepicker._findPos(input);
8293
+ $.datepicker._pos[1] += input.offsetHeight; // add the height
8294
+ }
8295
 
8296
+ isFixed = false;
8297
+ $(input).parents().each(function() {
8298
+ isFixed |= $(this).css("position") === "fixed";
8299
+ return !isFixed;
8300
+ });
8301
 
8302
+ offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
8303
+ $.datepicker._pos = null;
8304
+ //to avoid flashes on Firefox
8305
+ inst.dpDiv.empty();
8306
+ // determine sizing offscreen
8307
+ inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
8308
+ $.datepicker._updateDatepicker(inst);
8309
+ // fix width for dynamic number of date pickers
8310
+ // and adjust position before showing
8311
+ offset = $.datepicker._checkOffset(inst, offset, isFixed);
8312
+ inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
8313
+ "static" : (isFixed ? "fixed" : "absolute")), display: "none",
8314
+ left: offset.left + "px", top: offset.top + "px"});
8315
 
8316
+ if (!inst.inline) {
8317
+ showAnim = $.datepicker._get(inst, "showAnim");
8318
+ duration = $.datepicker._get(inst, "duration");
8319
+ inst.dpDiv.zIndex($(input).zIndex()+1);
8320
+ $.datepicker._datepickerShowing = true;
 
 
 
 
 
8321
 
8322
+ if ( $.effects && $.effects.effect[ showAnim ] ) {
8323
+ inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
 
8324
  } else {
8325
+ inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
 
 
 
 
 
 
 
 
 
 
 
 
8326
  }
 
8327
 
8328
+ if (inst.input.is(":visible") && !inst.input.is(":disabled")) {
8329
+ inst.input.focus();
8330
+ }
8331
+ $.datepicker._curInst = inst;
8332
  }
 
 
8333
  },
8334
 
8335
+ /* Generate the date picker content. */
8336
+ _updateDatepicker: function(inst) {
8337
+ this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
8338
+ instActive = inst; // for delegate hover events
8339
+ inst.dpDiv.empty().append(this._generateHTML(inst));
8340
+ this._attachHandlers(inst);
8341
+ inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
8342
+
8343
+ var origyearshtml,
8344
+ numMonths = this._getNumberOfMonths(inst),
8345
+ cols = numMonths[1],
8346
+ width = 17;
8347
+
8348
+ inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
8349
+ if (cols > 1) {
8350
+ inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
8351
+ }
8352
+ inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
8353
+ "Class"]("ui-datepicker-multi");
8354
+ inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
8355
+ "Class"]("ui-datepicker-rtl");
8356
+
8357
+ // #6694 - don't focus the input if it's already focused
8358
+ // this breaks the change event in IE
8359
+ if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
8360
+ inst.input.is(":visible") && !inst.input.is(":disabled") && inst.input[0] !== document.activeElement) {
8361
+ inst.input.focus();
8362
  }
 
8363
 
8364
+ // deffered render of the years select (to avoid flashes on Firefox)
8365
+ if( inst.yearshtml ){
8366
+ origyearshtml = inst.yearshtml;
8367
+ setTimeout(function(){
8368
+ //assure that inst.yearshtml didn't change.
8369
+ if( origyearshtml === inst.yearshtml && inst.yearshtml ){
8370
+ inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
8371
+ }
8372
+ origyearshtml = inst.yearshtml = null;
8373
+ }, 0);
8374
  }
8375
+ },
8376
 
8377
+ /* Retrieve the size of left and top borders for an element.
8378
+ * @param elem (jQuery object) the element of interest
8379
+ * @return (number[2]) the left and top borders
8380
+ */
8381
+ _getBorders: function(elem) {
8382
+ var convert = function(value) {
8383
+ return {thin: 1, medium: 2, thick: 3}[value] || value;
8384
+ };
8385
+ return [parseFloat(convert(elem.css("border-left-width"))),
8386
+ parseFloat(convert(elem.css("border-top-width")))];
8387
+ },
8388
 
8389
+ /* Check positioning to remain on screen. */
8390
+ _checkOffset: function(inst, offset, isFixed) {
8391
+ var dpWidth = inst.dpDiv.outerWidth(),
8392
+ dpHeight = inst.dpDiv.outerHeight(),
8393
+ inputWidth = inst.input ? inst.input.outerWidth() : 0,
8394
+ inputHeight = inst.input ? inst.input.outerHeight() : 0,
8395
+ viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
8396
+ viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
8397
 
8398
+ offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
8399
+ offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
8400
+ offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
 
 
 
 
8401
 
8402
+ // now check if datepicker is showing outside window viewport - move to a better place if so.
8403
+ offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
8404
+ Math.abs(offset.left + dpWidth - viewWidth) : 0);
8405
+ offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
8406
+ Math.abs(dpHeight + inputHeight) : 0);
8407
 
8408
+ return offset;
 
8409
  },
8410
 
8411
+ /* Find an object's position on the screen. */
8412
+ _findPos: function(obj) {
8413
+ var position,
8414
+ inst = this._getInst(obj),
8415
+ isRTL = this._get(inst, "isRTL");
8416
 
8417
+ while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
8418
+ obj = obj[isRTL ? "previousSibling" : "nextSibling"];
 
 
8419
  }
8420
 
8421
+ position = $(obj).offset();
8422
+ return [position.left, position.top];
 
 
 
 
 
 
 
 
 
 
8423
  },
8424
 
8425
+ /* Hide the date picker from view.
8426
+ * @param input element - the input field attached to the date picker
8427
+ */
8428
+ _hideDatepicker: function(input) {
8429
+ var showAnim, duration, postProcess, onClose,
8430
+ inst = this._curInst;
8431
+
8432
+ if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
8433
  return;
8434
  }
8435
 
8436
+ if (this._datepickerShowing) {
8437
+ showAnim = this._get(inst, "showAnim");
8438
+ duration = this._get(inst, "duration");
8439
+ postProcess = function() {
8440
+ $.datepicker._tidyDialog(inst);
8441
+ };
 
 
8442
 
8443
+ // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
8444
+ if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
8445
+ inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
8446
+ } else {
8447
+ inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
8448
+ (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
8449
+ }
8450
+
8451
+ if (!showAnim) {
8452
+ postProcess();
8453
+ }
8454
+ this._datepickerShowing = false;
8455
 
8456
+ onClose = this._get(inst, "onClose");
8457
+ if (onClose) {
8458
+ onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
8459
+ }
8460
 
8461
+ this._lastInput = null;
8462
+ if (this._inDialog) {
8463
+ this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
8464
+ if ($.blockUI) {
8465
+ $.unblockUI();
8466
+ $("body").append(this.dpDiv);
8467
+ }
8468
+ }
8469
+ this._inDialog = false;
8470
  }
8471
+ },
8472
 
8473
+ /* Tidy up after a dialog display. */
8474
+ _tidyDialog: function(inst) {
8475
+ inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
8476
  },
8477
 
8478
+ /* Close date picker if clicked elsewhere. */
8479
+ _checkExternalClick: function(event) {
8480
+ if (!$.datepicker._curInst) {
8481
+ return;
 
 
 
 
8482
  }
 
 
 
8483
 
8484
+ var $target = $(event.target),
8485
+ inst = $.datepicker._getInst($target[0]);
8486
+
8487
+ if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
8488
+ $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
8489
+ !$target.hasClass($.datepicker.markerClassName) &&
8490
+ !$target.closest("." + $.datepicker._triggerClass).length &&
8491
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
8492
+ ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
8493
+ $.datepicker._hideDatepicker();
8494
+ }
8495
+ },
8496
 
8497
+ /* Adjust one of the date sub-fields. */
8498
+ _adjustDate: function(id, offset, period) {
8499
+ var target = $(id),
8500
+ inst = this._getInst(target[0]);
8501
 
8502
+ if (this._isDisabledDatepicker(target[0])) {
 
 
8503
  return;
8504
  }
8505
+ this._adjustInstDate(inst, offset +
8506
+ (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
8507
+ period);
8508
+ this._updateDatepicker(inst);
8509
+ },
8510
 
8511
+ /* Action for current link. */
8512
+ _gotoToday: function(id) {
8513
+ var date,
8514
+ target = $(id),
8515
+ inst = this._getInst(target[0]);
8516
 
8517
+ if (this._get(inst, "gotoCurrent") && inst.currentDay) {
8518
+ inst.selectedDay = inst.currentDay;
8519
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth;
8520
+ inst.drawYear = inst.selectedYear = inst.currentYear;
8521
+ } else {
8522
+ date = new Date();
8523
+ inst.selectedDay = date.getDate();
8524
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
8525
+ inst.drawYear = inst.selectedYear = date.getFullYear();
8526
  }
8527
+ this._notifyChange(inst);
8528
+ this._adjustDate(target);
8529
+ },
8530
 
8531
+ /* Action for selecting a new month/year. */
8532
+ _selectMonthYear: function(id, select, period) {
8533
+ var target = $(id),
8534
+ inst = this._getInst(target[0]);
8535
 
8536
+ inst["selected" + (period === "M" ? "Month" : "Year")] =
8537
+ inst["draw" + (period === "M" ? "Month" : "Year")] =
8538
+ parseInt(select.options[select.selectedIndex].value,10);
8539
 
8540
+ this._notifyChange(inst);
8541
+ this._adjustDate(target);
8542
+ },
8543
 
8544
+ /* Action for selecting a day. */
8545
+ _selectDay: function(id, month, year, td) {
8546
+ var inst,
8547
+ target = $(id);
 
 
 
 
 
8548
 
8549
+ if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
8550
+ return;
8551
+ }
 
 
 
 
 
 
 
 
8552
 
8553
+ inst = this._getInst(target[0]);
8554
+ inst.selectedDay = inst.currentDay = $("a", td).html();
8555
+ inst.selectedMonth = inst.currentMonth = month;
8556
+ inst.selectedYear = inst.currentYear = year;
8557
+ this._selectDate(id, this._formatDate(inst,
8558
+ inst.currentDay, inst.currentMonth, inst.currentYear));
8559
+ },
8560
 
8561
+ /* Erase the input field and hide the date picker. */
8562
+ _clearDate: function(id) {
8563
+ var target = $(id);
8564
+ this._selectDate(target, "");
8565
  },
8566
 
8567
+ /* Update the input field with the selected date. */
8568
+ _selectDate: function(id, dateStr) {
8569
+ var onSelect,
8570
+ target = $(id),
8571
+ inst = this._getInst(target[0]);
8572
 
8573
+ dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
8574
+ if (inst.input) {
8575
+ inst.input.val(dateStr);
8576
+ }
8577
+ this._updateAlternate(inst);
8578
 
8579
+ onSelect = this._get(inst, "onSelect");
8580
+ if (onSelect) {
8581
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
8582
+ } else if (inst.input) {
8583
+ inst.input.trigger("change"); // fire the change event
8584
  }
8585
 
8586
+ if (inst.inline){
8587
+ this._updateDatepicker(inst);
8588
+ } else {
8589
+ this._hideDatepicker();
8590
+ this._lastInput = inst.input[0];
8591
+ if (typeof(inst.input[0]) !== "object") {
8592
+ inst.input.focus(); // restore focus
8593
+ }
8594
+ this._lastInput = null;
8595
+ }
8596
  },
8597
 
8598
+ /* Update any alternate field to synchronise with the main field. */
8599
+ _updateAlternate: function(inst) {
8600
+ var altFormat, date, dateStr,
8601
+ altField = this._get(inst, "altField");
8602
+
8603
+ if (altField) { // update alternate field too
8604
+ altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
8605
+ date = this._getDate(inst);
8606
+ dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
8607
+ $(altField).each(function() { $(this).val(dateStr); });
8608
+ }
8609
  },
8610
 
8611
+ /* Set as beforeShowDay function to prevent selection of weekends.
8612
+ * @param date Date - the date to customise
8613
+ * @return [boolean, string] - is this date selectable?, what is its CSS class?
8614
+ */
8615
+ noWeekends: function(date) {
8616
+ var day = date.getDay();
8617
+ return [(day > 0 && day < 6), ""];
8618
+ },
8619
 
8620
+ /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
8621
+ * @param date Date - the date to get the week for
8622
+ * @return number - the number of the week within the year that contains this date
8623
+ */
8624
+ iso8601Week: function(date) {
8625
+ var time,
8626
+ checkDate = new Date(date.getTime());
8627
 
8628
+ // Find Thursday of this week starting on Monday
8629
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
 
8630
 
8631
+ time = checkDate.getTime();
8632
+ checkDate.setMonth(0); // Compare with Jan 1
8633
+ checkDate.setDate(1);
8634
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
8635
+ },
 
 
 
8636
 
8637
+ /* Parse a string value into a date object.
8638
+ * See formatDate below for the possible formats.
8639
+ *
8640
+ * @param format string - the expected format of the date
8641
+ * @param value string - the date in the above format
8642
+ * @param settings Object - attributes include:
8643
+ * shortYearCutoff number - the cutoff year for determining the century (optional)
8644
+ * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8645
+ * dayNames string[7] - names of the days from Sunday (optional)
8646
+ * monthNamesShort string[12] - abbreviated names of the months (optional)
8647
+ * monthNames string[12] - names of the months (optional)
8648
+ * @return Date - the extracted date value or null if value is blank
8649
+ */
8650
+ parseDate: function (format, value, settings) {
8651
+ if (format == null || value == null) {
8652
+ throw "Invalid arguments";
8653
+ }
8654
+
8655
+ value = (typeof value === "object" ? value.toString() : value + "");
8656
+ if (value === "") {
8657
+ return null;
8658
+ }
8659
+
8660
+ var iFormat, dim, extra,
8661
+ iValue = 0,
8662
+ shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
8663
+ shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
8664
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
8665
+ dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8666
+ dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8667
+ monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8668
+ monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8669
+ year = -1,
8670
+ month = -1,
8671
+ day = -1,
8672
+ doy = -1,
8673
+ literal = false,
8674
+ date,
8675
+ // Check whether a format character is doubled
8676
+ lookAhead = function(match) {
8677
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8678
+ if (matches) {
8679
+ iFormat++;
8680
+ }
8681
+ return matches;
8682
+ },
8683
+ // Extract a number from the string value
8684
+ getNumber = function(match) {
8685
+ var isDoubled = lookAhead(match),
8686
+ size = (match === "@" ? 14 : (match === "!" ? 20 :
8687
+ (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
8688
+ digits = new RegExp("^\\d{1," + size + "}"),
8689
+ num = value.substring(iValue).match(digits);
8690
+ if (!num) {
8691
+ throw "Missing number at position " + iValue;
8692
+ }
8693
+ iValue += num[0].length;
8694
+ return parseInt(num[0], 10);
8695
+ },
8696
+ // Extract a name from the string value and convert to an index
8697
+ getName = function(match, shortNames, longNames) {
8698
+ var index = -1,
8699
+ names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
8700
+ return [ [k, v] ];
8701
+ }).sort(function (a, b) {
8702
+ return -(a[1].length - b[1].length);
8703
+ });
8704
+
8705
+ $.each(names, function (i, pair) {
8706
+ var name = pair[1];
8707
+ if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
8708
+ index = pair[0];
8709
+ iValue += name.length;
8710
+ return false;
8711
+ }
8712
+ });
8713
+ if (index !== -1) {
8714
+ return index + 1;
8715
+ } else {
8716
+ throw "Unknown name at position " + iValue;
8717
+ }
8718
+ },
8719
+ // Confirm that a literal character matches the string value
8720
+ checkLiteral = function() {
8721
+ if (value.charAt(iValue) !== format.charAt(iFormat)) {
8722
+ throw "Unexpected literal at position " + iValue;
8723
+ }
8724
+ iValue++;
8725
+ };
8726
 
8727
+ for (iFormat = 0; iFormat < format.length; iFormat++) {
8728
+ if (literal) {
8729
+ if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8730
+ literal = false;
8731
+ } else {
8732
+ checkLiteral();
8733
+ }
8734
+ } else {
8735
+ switch (format.charAt(iFormat)) {
8736
+ case "d":
8737
+ day = getNumber("d");
8738
+ break;
8739
+ case "D":
8740
+ getName("D", dayNamesShort, dayNames);
8741
+ break;
8742
+ case "o":
8743
+ doy = getNumber("o");
8744
+ break;
8745
+ case "m":
8746
+ month = getNumber("m");
8747
+ break;
8748
+ case "M":
8749
+ month = getName("M", monthNamesShort, monthNames);
8750
+ break;
8751
+ case "y":
8752
+ year = getNumber("y");
8753
+ break;
8754
+ case "@":
8755
+ date = new Date(getNumber("@"));
8756
+ year = date.getFullYear();
8757
+ month = date.getMonth() + 1;
8758
+ day = date.getDate();
8759
+ break;
8760
+ case "!":
8761
+ date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
8762
+ year = date.getFullYear();
8763
+ month = date.getMonth() + 1;
8764
+ day = date.getDate();
8765
+ break;
8766
+ case "'":
8767
+ if (lookAhead("'")){
8768
+ checkLiteral();
8769
+ } else {
8770
+ literal = true;
8771
+ }
8772
+ break;
8773
+ default:
8774
+ checkLiteral();
8775
  }
8776
  }
8777
+ }
 
 
 
8778
 
8779
+ if (iValue < value.length){
8780
+ extra = value.substr(iValue);
8781
+ if (!/^\s+/.test(extra)) {
8782
+ throw "Extra/unparsed characters found in date: " + extra;
8783
+ }
 
 
 
 
 
 
 
8784
  }
8785
 
8786
+ if (year === -1) {
8787
+ year = new Date().getFullYear();
8788
+ } else if (year < 100) {
8789
+ year += new Date().getFullYear() - new Date().getFullYear() % 100 +
8790
+ (year <= shortYearCutoff ? 0 : -100);
8791
+ }
8792
 
8793
+ if (doy > -1) {
8794
+ month = 1;
8795
+ day = doy;
8796
+ do {
8797
+ dim = this._getDaysInMonth(year, month - 1);
8798
+ if (day <= dim) {
8799
+ break;
8800
+ }
8801
+ month++;
8802
+ day -= dim;
8803
+ } while (true);
8804
+ }
 
 
8805
 
8806
+ date = this._daylightSavingAdjust(new Date(year, month - 1, day));
8807
+ if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
8808
+ throw "Invalid date"; // E.g. 31/02/00
8809
+ }
8810
+ return date;
8811
+ },
8812
 
8813
+ /* Standard date formats. */
8814
+ ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
8815
+ COOKIE: "D, dd M yy",
8816
+ ISO_8601: "yy-mm-dd",
8817
+ RFC_822: "D, d M y",
8818
+ RFC_850: "DD, dd-M-y",
8819
+ RFC_1036: "D, d M y",
8820
+ RFC_1123: "D, d M yy",
8821
+ RFC_2822: "D, d M yy",
8822
+ RSS: "D, d M y", // RFC 822
8823
+ TICKS: "!",
8824
+ TIMESTAMP: "@",
8825
+ W3C: "yy-mm-dd", // ISO 8601
8826
 
8827
+ _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
8828
+ Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
 
 
8829
 
8830
+ /* Format a date object into a string value.
8831
+ * The format can be combinations of the following:
8832
+ * d - day of month (no leading zero)
8833
+ * dd - day of month (two digit)
8834
+ * o - day of year (no leading zeros)
8835
+ * oo - day of year (three digit)
8836
+ * D - day name short
8837
+ * DD - day name long
8838
+ * m - month of year (no leading zero)
8839
+ * mm - month of year (two digit)
8840
+ * M - month name short
8841
+ * MM - month name long
8842
+ * y - year (two digit)
8843
+ * yy - year (four digit)
8844
+ * @ - Unix timestamp (ms since 01/01/1970)
8845
+ * ! - Windows ticks (100ns since 01/01/0001)
8846
+ * "..." - literal text
8847
+ * '' - single quote
8848
+ *
8849
+ * @param format string - the desired format of the date
8850
+ * @param date Date - the date value to format
8851
+ * @param settings Object - attributes include:
8852
+ * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8853
+ * dayNames string[7] - names of the days from Sunday (optional)
8854
+ * monthNamesShort string[12] - abbreviated names of the months (optional)
8855
+ * monthNames string[12] - names of the months (optional)
8856
+ * @return string - the date in the above format
8857
+ */
8858
+ formatDate: function (format, date, settings) {
8859
+ if (!date) {
8860
+ return "";
8861
+ }
8862
+
8863
+ var iFormat,
8864
+ dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8865
+ dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8866
+ monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8867
+ monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8868
+ // Check whether a format character is doubled
8869
+ lookAhead = function(match) {
8870
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8871
+ if (matches) {
8872
+ iFormat++;
8873
+ }
8874
+ return matches;
8875
+ },
8876
+ // Format a number, with leading zero if necessary
8877
+ formatNumber = function(match, value, len) {
8878
+ var num = "" + value;
8879
+ if (lookAhead(match)) {
8880
+ while (num.length < len) {
8881
+ num = "0" + num;
8882
+ }
8883
+ }
8884
+ return num;
8885
+ },
8886
+ // Format a name, short or long as requested
8887
+ formatName = function(match, value, shortNames, longNames) {
8888
+ return (lookAhead(match) ? longNames[value] : shortNames[value]);
8889
+ },
8890
+ output = "",
8891
+ literal = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8892
 
8893
+ if (date) {
8894
+ for (iFormat = 0; iFormat < format.length; iFormat++) {
8895
+ if (literal) {
8896
+ if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8897
+ literal = false;
8898
+ } else {
8899
+ output += format.charAt(iFormat);
8900
+ }
8901
+ } else {
8902
+ switch (format.charAt(iFormat)) {
8903
+ case "d":
8904
+ output += formatNumber("d", date.getDate(), 2);
8905
+ break;
8906
+ case "D":
8907
+ output += formatName("D", date.getDay(), dayNamesShort, dayNames);
8908
+ break;
8909
+ case "o":
8910
+ output += formatNumber("o",
8911
+ Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
8912
+ break;
8913
+ case "m":
8914
+ output += formatNumber("m", date.getMonth() + 1, 2);
8915
+ break;
8916
+ case "M":
8917
+ output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
8918
+ break;
8919
+ case "y":
8920
+ output += (lookAhead("y") ? date.getFullYear() :
8921
+ (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
8922
+ break;
8923
+ case "@":
8924
+ output += date.getTime();
8925
+ break;
8926
+ case "!":
8927
+ output += date.getTime() * 10000 + this._ticksTo1970;
8928
+ break;
8929
+ case "'":
8930
+ if (lookAhead("'")) {
8931
+ output += "'";
8932
+ } else {
8933
+ literal = true;
8934
+ }
8935
+ break;
8936
+ default:
8937
+ output += format.charAt(iFormat);
8938
+ }
8939
+ }
8940
+ }
8941
+ }
8942
+ return output;
8943
  },
8944
 
8945
+ /* Extract all possible characters from the date format. */
8946
+ _possibleChars: function (format) {
8947
+ var iFormat,
8948
+ chars = "",
8949
+ literal = false,
8950
+ // Check whether a format character is doubled
8951
+ lookAhead = function(match) {
8952
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8953
+ if (matches) {
8954
+ iFormat++;
8955
+ }
8956
+ return matches;
8957
+ };
8958
 
8959
+ for (iFormat = 0; iFormat < format.length; iFormat++) {
8960
+ if (literal) {
8961
+ if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8962
+ literal = false;
8963
+ } else {
8964
+ chars += format.charAt(iFormat);
8965
+ }
8966
+ } else {
8967
+ switch (format.charAt(iFormat)) {
8968
+ case "d": case "m": case "y": case "@":
8969
+ chars += "0123456789";
8970
+ break;
8971
+ case "D": case "M":
8972
+ return null; // Accept anything
8973
+ case "'":
8974
+ if (lookAhead("'")) {
8975
+ chars += "'";
8976
+ } else {
8977
+ literal = true;
8978
+ }
8979
+ break;
8980
+ default:
8981
+ chars += format.charAt(iFormat);
8982
  }
8983
  }
8984
  }
8985
+ return chars;
 
 
 
 
 
 
 
 
 
 
 
 
8986
  },
8987
 
8988
+ /* Get a setting value, defaulting if necessary. */
8989
+ _get: function(inst, name) {
8990
+ return inst.settings[name] !== undefined ?
8991
+ inst.settings[name] : this._defaults[name];
 
 
 
 
 
8992
  },
8993
 
8994
+ /* Parse existing date and initialise date picker. */
8995
+ _setDateFromField: function(inst, noDefault) {
8996
+ if (inst.input.val() === inst.lastVal) {
 
 
 
8997
  return;
 
 
 
 
 
 
 
 
 
 
 
 
 
8998
  }
 
8999