Mage_Downloader - Version 1.6.1.0

Version Notes

1.6.1.0

Download this release

Release Info

Developer Magento Core Team
Extension Mage_Downloader
Version 1.6.1.0
Comparing to
See all releases


Version 1.6.1.0

Files changed (136) hide show
  1. .htaccess +186 -0
  2. downloader/.htaccess +11 -0
  3. downloader/Maged/Connect.php +467 -0
  4. downloader/Maged/Connect/Frontend.php +149 -0
  5. downloader/Maged/Controller.php +977 -0
  6. downloader/Maged/Exception.php +37 -0
  7. downloader/Maged/Model.php +99 -0
  8. downloader/Maged/Model/Config.php +84 -0
  9. downloader/Maged/Model/Config/Abstract.php +134 -0
  10. downloader/Maged/Model/Config/Community.php +112 -0
  11. downloader/Maged/Model/Config/Interface.php +90 -0
  12. downloader/Maged/Model/Connect.php +491 -0
  13. downloader/Maged/Model/Connect/Request.php +43 -0
  14. downloader/Maged/Model/Dowloader.php +30 -0
  15. downloader/Maged/Model/Session.php +224 -0
  16. downloader/Maged/View.php +155 -0
  17. downloader/config.ini +1 -0
  18. downloader/index.php +36 -0
  19. downloader/js/prototype.js +3277 -0
  20. downloader/lib/Mage/Archive.php +222 -0
  21. downloader/lib/Mage/Archive/Abstract.php +84 -0
  22. downloader/lib/Mage/Archive/Bz.php +79 -0
  23. downloader/lib/Mage/Archive/Gz.php +77 -0
  24. downloader/lib/Mage/Archive/Interface.php +53 -0
  25. downloader/lib/Mage/Archive/Tar.php +372 -0
  26. downloader/lib/Mage/Autoload/Simple.php +52 -0
  27. downloader/lib/Mage/Connect/Channel/Generator.php +63 -0
  28. downloader/lib/Mage/Connect/Channel/Parser.php +25 -0
  29. downloader/lib/Mage/Connect/Channel/VO.php +113 -0
  30. downloader/lib/Mage/Connect/Command.php +390 -0
  31. downloader/lib/Mage/Connect/Command/Channels.php +189 -0
  32. downloader/lib/Mage/Connect/Command/Channels_Header.php +104 -0
  33. downloader/lib/Mage/Connect/Command/Config.php +213 -0
  34. downloader/lib/Mage/Connect/Command/Config_Header.php +100 -0
  35. downloader/lib/Mage/Connect/Command/Install.php +567 -0
  36. downloader/lib/Mage/Connect/Command/Install_Header.php +237 -0
  37. downloader/lib/Mage/Connect/Command/Package.php +204 -0
  38. downloader/lib/Mage/Connect/Command/Package_Header.php +76 -0
  39. downloader/lib/Mage/Connect/Command/Registry.php +364 -0
  40. downloader/lib/Mage/Connect/Command/Registry_Header.php +84 -0
  41. downloader/lib/Mage/Connect/Command/Remote.php +231 -0
  42. downloader/lib/Mage/Connect/Command/Remote_Header.php +88 -0
  43. downloader/lib/Mage/Connect/Config.php +464 -0
  44. downloader/lib/Mage/Connect/Converter.php +336 -0
  45. downloader/lib/Mage/Connect/Frontend.php +251 -0
  46. downloader/lib/Mage/Connect/Frontend/CLI.php +456 -0
  47. downloader/lib/Mage/Connect/Ftp.php +523 -0
  48. downloader/lib/Mage/Connect/Loader.php +51 -0
  49. downloader/lib/Mage/Connect/Loader/Ftp.php +155 -0
  50. downloader/lib/Mage/Connect/Package.php +1499 -0
  51. downloader/lib/Mage/Connect/Package/Extension.php +1 -0
  52. downloader/lib/Mage/Connect/Package/Hotfix.php +137 -0
  53. downloader/lib/Mage/Connect/Package/Maintainer.php +1 -0
  54. downloader/lib/Mage/Connect/Package/Reader.php +150 -0
  55. downloader/lib/Mage/Connect/Package/Target.php +126 -0
  56. downloader/lib/Mage/Connect/Package/VO.php +96 -0
  57. downloader/lib/Mage/Connect/Package/Writer.php +177 -0
  58. downloader/lib/Mage/Connect/Packager.php +909 -0
  59. downloader/lib/Mage/Connect/Repository.php +1 -0
  60. downloader/lib/Mage/Connect/Repository/Abstract.php +1 -0
  61. downloader/lib/Mage/Connect/Repository/Channel.php +1 -0
  62. downloader/lib/Mage/Connect/Repository/Channel/Abstract.php +1 -0
  63. downloader/lib/Mage/Connect/Repository/Channel/Commercial.php +1 -0
  64. downloader/lib/Mage/Connect/Repository/Channel/Community.php +1 -0
  65. downloader/lib/Mage/Connect/Repository/Channel/Core.php +1 -0
  66. downloader/lib/Mage/Connect/Repository/Local.php +1 -0
  67. downloader/lib/Mage/Connect/Rest.php +366 -0
  68. downloader/lib/Mage/Connect/Singleconfig.php +934 -0
  69. downloader/lib/Mage/Connect/Structures/Graph.php +248 -0
  70. downloader/lib/Mage/Connect/Structures/Node.php +257 -0
  71. downloader/lib/Mage/Connect/Validator.php +440 -0
  72. downloader/lib/Mage/DB/Exception.php +36 -0
  73. downloader/lib/Mage/DB/Mysqli.php +532 -0
  74. downloader/lib/Mage/Exception.php +35 -0
  75. downloader/lib/Mage/HTTP/Client.php +84 -0
  76. downloader/lib/Mage/HTTP/Client/Curl.php +556 -0
  77. downloader/lib/Mage/HTTP/Client/Socket.php +537 -0
  78. downloader/lib/Mage/HTTP/IClient.php +145 -0
  79. downloader/lib/Mage/System/Args.php +102 -0
  80. downloader/lib/Mage/System/Dirs.php +104 -0
  81. downloader/lib/Mage/Xml/Generator.php +114 -0
  82. downloader/lib/Mage/Xml/Parser.php +115 -0
  83. downloader/mage.php +156 -0
  84. downloader/skin/boxes.css +217 -0
  85. downloader/skin/ie7boxes.css +27 -0
  86. downloader/skin/ieboxes.css +29 -0
  87. downloader/skin/images/Magento_Connect.jpg +0 -0
  88. downloader/skin/images/ajax-loader-tr.gif +0 -0
  89. downloader/skin/images/btn_bg.gif +0 -0
  90. downloader/skin/images/header_bg.gif +0 -0
  91. downloader/skin/images/logo.gif +0 -0
  92. downloader/skin/images/nav_bg.gif +0 -0
  93. downloader/skin/images/nav_separator.gif +0 -0
  94. downloader/skin/install/boxes.css +414 -0
  95. downloader/skin/install/clears.css +70 -0
  96. downloader/skin/install/ie7minus.css +40 -0
  97. downloader/skin/install/iestyles.css +78 -0
  98. downloader/skin/install/images/error_msg_icon.gif +0 -0
  99. downloader/skin/install/images/footer_bg.gif +0 -0
  100. downloader/skin/install/images/footer_container_bg.gif +0 -0
  101. downloader/skin/install/images/footer_info_separator.gif +0 -0
  102. downloader/skin/install/images/footer_informational_bg.gif +0 -0
  103. downloader/skin/install/images/footer_left.gif +0 -0
  104. downloader/skin/install/images/footer_legality_bg.gif +0 -0
  105. downloader/skin/install/images/footer_right.gif +0 -0
  106. downloader/skin/install/images/header_bg.gif +0 -0
  107. downloader/skin/install/images/header_nav_bg.gif +0 -0
  108. downloader/skin/install/images/header_top_bg.jpg +0 -0
  109. downloader/skin/install/images/header_top_container_bg.jpg +0 -0
  110. downloader/skin/install/images/logo.gif +0 -0
  111. downloader/skin/install/images/main_bg.gif +0 -0
  112. downloader/skin/install/images/main_container_bg.gif +0 -0
  113. downloader/skin/install/images/note_msg_icon.gif +0 -0
  114. downloader/skin/install/images/success_msg_icon.gif +0 -0
  115. downloader/skin/install/images/validation_advice_bg.gif +0 -0
  116. downloader/skin/install/reset.css +83 -0
  117. downloader/template/.htaccess +2 -0
  118. downloader/template/connect/iframe.phtml +121 -0
  119. downloader/template/connect/packages.phtml +221 -0
  120. downloader/template/connect/packages_prepare.phtml +78 -0
  121. downloader/template/exception.phtml +36 -0
  122. downloader/template/footer.phtml +40 -0
  123. downloader/template/header.phtml +73 -0
  124. downloader/template/index.phtml +36 -0
  125. downloader/template/install/download.phtml +184 -0
  126. downloader/template/install/footer.phtml +49 -0
  127. downloader/template/install/header.phtml +99 -0
  128. downloader/template/install/writable.phtml +40 -0
  129. downloader/template/login.phtml +43 -0
  130. downloader/template/messages.phtml +39 -0
  131. downloader/template/noroute.phtml +31 -0
  132. downloader/template/settings.phtml +166 -0
  133. downloader/template/writable.phtml +35 -0
  134. index.php +80 -0
  135. mage +54 -0
  136. package.xml +18 -0
.htaccess ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ############################################
2
+ ## uncomment these lines for CGI mode
3
+ ## make sure to specify the correct cgi php binary file name
4
+ ## it might be /cgi-bin/php-cgi
5
+
6
+ # Action php5-cgi /cgi-bin/php5-cgi
7
+ # AddHandler php5-cgi .php
8
+
9
+ ############################################
10
+ ## GoDaddy specific options
11
+
12
+ # Options -MultiViews
13
+
14
+ ## you might also need to add this line to php.ini
15
+ ## cgi.fix_pathinfo = 1
16
+ ## if it still doesn't work, rename php.ini to php5.ini
17
+
18
+ ############################################
19
+ ## this line is specific for 1and1 hosting
20
+
21
+ #AddType x-mapp-php5 .php
22
+ #AddHandler x-mapp-php5 .php
23
+
24
+ ############################################
25
+ ## default index file
26
+
27
+ DirectoryIndex index.php
28
+
29
+ <IfModule mod_php5.c>
30
+
31
+ ############################################
32
+ ## adjust memory limit
33
+
34
+ # php_value memory_limit 64M
35
+ php_value memory_limit 256M
36
+ php_value max_execution_time 18000
37
+
38
+ ############################################
39
+ ## disable magic quotes for php request vars
40
+
41
+ php_flag magic_quotes_gpc off
42
+
43
+ ############################################
44
+ ## disable automatic session start
45
+ ## before autoload was initialized
46
+
47
+ php_flag session.auto_start off
48
+
49
+ ############################################
50
+ ## enable resulting html compression
51
+
52
+ #php_flag zlib.output_compression on
53
+
54
+ ###########################################
55
+ # disable user agent verification to not break multiple image upload
56
+
57
+ php_flag suhosin.session.cryptua off
58
+
59
+ ###########################################
60
+ # turn off compatibility with PHP4 when dealing with objects
61
+
62
+ php_flag zend.ze1_compatibility_mode Off
63
+
64
+ </IfModule>
65
+
66
+ <IfModule mod_security.c>
67
+ ###########################################
68
+ # disable POST processing to not break multiple image upload
69
+
70
+ SecFilterEngine Off
71
+ SecFilterScanPOST Off
72
+ </IfModule>
73
+
74
+ <IfModule mod_deflate.c>
75
+
76
+ ############################################
77
+ ## enable apache served files compression
78
+ ## http://developer.yahoo.com/performance/rules.html#gzip
79
+
80
+ # Insert filter on all content
81
+ ###SetOutputFilter DEFLATE
82
+ # Insert filter on selected content types only
83
+ #AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript
84
+
85
+ # Netscape 4.x has some problems...
86
+ #BrowserMatch ^Mozilla/4 gzip-only-text/html
87
+
88
+ # Netscape 4.06-4.08 have some more problems
89
+ #BrowserMatch ^Mozilla/4\.0[678] no-gzip
90
+
91
+ # MSIE masquerades as Netscape, but it is fine
92
+ #BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
93
+
94
+ # Don't compress images
95
+ #SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
96
+
97
+ # Make sure proxies don't deliver the wrong content
98
+ #Header append Vary User-Agent env=!dont-vary
99
+
100
+ </IfModule>
101
+
102
+ <IfModule mod_ssl.c>
103
+
104
+ ############################################
105
+ ## make HTTPS env vars available for CGI mode
106
+
107
+ SSLOptions StdEnvVars
108
+
109
+ </IfModule>
110
+
111
+ <IfModule mod_rewrite.c>
112
+
113
+ ############################################
114
+ ## enable rewrites
115
+
116
+ Options +FollowSymLinks
117
+ RewriteEngine on
118
+
119
+ ############################################
120
+ ## you can put here your magento root folder
121
+ ## path relative to web root
122
+
123
+ #RewriteBase /magento/
124
+
125
+ ############################################
126
+ ## workaround for HTTP authorization
127
+ ## in CGI environment
128
+
129
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
130
+
131
+ ############################################
132
+ ## always send 404 on missing files in these folders
133
+
134
+ RewriteCond %{REQUEST_URI} !^/(media|skin|js)/
135
+
136
+ ############################################
137
+ ## never rewrite for existing files, directories and links
138
+
139
+ RewriteCond %{REQUEST_FILENAME} !-f
140
+ RewriteCond %{REQUEST_FILENAME} !-d
141
+ RewriteCond %{REQUEST_FILENAME} !-l
142
+
143
+ ############################################
144
+ ## rewrite everything else to index.php
145
+
146
+ RewriteRule .* index.php [L]
147
+
148
+ </IfModule>
149
+
150
+
151
+ ############################################
152
+ ## Prevent character encoding issues from server overrides
153
+ ## If you still have problems, use the second line instead
154
+
155
+ AddDefaultCharset Off
156
+ #AddDefaultCharset UTF-8
157
+
158
+ <IfModule mod_expires.c>
159
+
160
+ ############################################
161
+ ## Add default Expires header
162
+ ## http://developer.yahoo.com/performance/rules.html#expires
163
+
164
+ ExpiresDefault "access plus 1 year"
165
+
166
+ </IfModule>
167
+
168
+ ############################################
169
+ ## By default allow all access
170
+
171
+ Order allow,deny
172
+ Allow from all
173
+
174
+ ###########################################
175
+ ## Deny access to release notes to prevent disclosure of the installed Magento version
176
+
177
+ <Files RELEASE_NOTES.txt>
178
+ order allow,deny
179
+ deny from all
180
+ </Files>
181
+
182
+ ############################################
183
+ ## If running in cluster environment, uncomment this
184
+ ## http://developer.yahoo.com/performance/rules.html#etags
185
+
186
+ #FileETag none
downloader/.htaccess ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <IfModule mod_deflate.c>
2
+
3
+ RemoveOutputFilter DEFLATE
4
+ RemoveOutputFilter GZIP
5
+
6
+ </IfModule>
7
+
8
+ <Files ~ "\.(cfg|ini|xml)$">
9
+ order allow,deny
10
+ deny from all
11
+ </Files>
downloader/Maged/Connect.php ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ error_reporting(E_ALL & ~E_NOTICE);
28
+
29
+ // just a shortcut
30
+ if (!defined('DS')) {
31
+ define('DS', DIRECTORY_SEPARATOR);
32
+ }
33
+
34
+ // add Mage lib in include_path if needed
35
+ $_includePath = get_include_path();
36
+ $_libDir = dirname(dirname(__FILE__)) . DS . 'lib';
37
+ if (strpos($_includePath, $_libDir) === false) {
38
+ if (substr($_includePath, 0, 2) === '.' . PATH_SEPARATOR) {
39
+ $_includePath = '.' . PATH_SEPARATOR . $_libDir . PATH_SEPARATOR . substr($_includePath, 2);
40
+ } else {
41
+ $_includePath = $_libDir . PATH_SEPARATOR . $_includePath;
42
+ }
43
+ set_include_path($_includePath);
44
+ }
45
+
46
+ /**
47
+ * Class for connect
48
+ *
49
+ * @category Mage
50
+ * @package Mage_Connect
51
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
52
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
53
+ */
54
+ class Maged_Connect
55
+ {
56
+
57
+ /**
58
+ * Object of config
59
+ *
60
+ * @var Mage_Connect_Config
61
+ */
62
+ protected $_config;
63
+
64
+ /**
65
+ * Object of single config
66
+ *
67
+ * @var Mage_Connect_Singleconfig
68
+ */
69
+ protected $_sconfig;
70
+
71
+ /**
72
+ * Object of frontend
73
+ *
74
+ * @var Mage_Connect_Frontend
75
+ */
76
+ protected $_frontend;
77
+
78
+ /**
79
+ * Internal cache for command objects
80
+ *
81
+ * @var array
82
+ */
83
+ protected $_cmdCache = array();
84
+
85
+ /**
86
+ * Instance of class
87
+ *
88
+ * @var Maged_Connect
89
+ */
90
+ static protected $_instance;
91
+
92
+ /**
93
+ * Constructor
94
+ */
95
+ public function __construct()
96
+ {
97
+ $this->getConfig();
98
+ $this->getSingleConfig();
99
+ $this->getFrontend();
100
+ }
101
+
102
+ /**
103
+ * Initialize instance
104
+ *
105
+ * @return Maged_Connect
106
+ */
107
+ public static function getInstance()
108
+ {
109
+ if (!self::$_instance) {
110
+ self::$_instance = new self;
111
+ }
112
+ return self::$_instance;
113
+ }
114
+
115
+ /**
116
+ * Retrieve object of config and set it to Mage_Connect_Command
117
+ *
118
+ * @return Mage_Connect_Config
119
+ */
120
+ public function getConfig()
121
+ {
122
+ if (!$this->_config) {
123
+ $this->_config = new Mage_Connect_Config();
124
+ $ftp=$this->_config->__get('remote_config');
125
+ if(!empty($ftp)){
126
+ $packager = new Mage_Connect_Packager();
127
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
128
+ $this->_config=$config;
129
+ $this->_sconfig=$cache;
130
+ }
131
+ $this->_config->magento_root = dirname(dirname(__FILE__)).DS.'..';
132
+ Mage_Connect_Command::setConfigObject($this->_config);
133
+ }
134
+ return $this->_config;
135
+ }
136
+
137
+ /**
138
+ * Retrieve object of single config and set it to Mage_Connect_Command
139
+ *
140
+ * @param bool $reload
141
+ * @return Mage_Connect_Singleconfig
142
+ */
143
+ public function getSingleConfig($reload = false)
144
+ {
145
+ if(!$this->_sconfig || $reload) {
146
+ $this->_sconfig = new Mage_Connect_Singleconfig($this->getConfig()->magento_root . DIRECTORY_SEPARATOR . $this->getConfig()->downloader_path . DIRECTORY_SEPARATOR . Mage_Connect_Singleconfig::DEFAULT_SCONFIG_FILENAME);
147
+ }
148
+ Mage_Connect_Command::setSconfig($this->_sconfig);
149
+ return $this->_sconfig;
150
+
151
+ }
152
+
153
+ /**
154
+ * Retrieve object of frontend and set it to Mage_Connect_Command
155
+ *
156
+ * @return Maged_Connect_Frontend
157
+ */
158
+ public function getFrontend()
159
+ {
160
+ if (!$this->_frontend) {
161
+ $this->_frontend = new Maged_Connect_Frontend();
162
+ Mage_Connect_Command::setFrontendObject($this->_frontend);
163
+ }
164
+ return $this->_frontend;
165
+ }
166
+
167
+ /**
168
+ * Retrieve lof from frontend
169
+ *
170
+ * @return array
171
+ */
172
+ public function getLog()
173
+ {
174
+ return $this->getFrontend()->getLog();
175
+ }
176
+
177
+ /**
178
+ * Retrieve output from frontend
179
+ *
180
+ * @return array
181
+ */
182
+ public function getOutput()
183
+ {
184
+ return $this->getFrontend()->getOutput();
185
+ }
186
+
187
+ /**
188
+ * Clean registry
189
+ *
190
+ * @return Maged_Connect
191
+ */
192
+ public function cleanSconfig()
193
+ {
194
+ $this->getSingleConfig()->clear();
195
+ return $this;
196
+ }
197
+
198
+ /**
199
+ * Delete directory recursively
200
+ *
201
+ * @param string $path
202
+ * @return Maged_Connect
203
+ */
204
+ public function delTree($path) {
205
+ if (@is_dir($path)) {
206
+ $entries = @scandir($path);
207
+ foreach ($entries as $entry) {
208
+ if ($entry != '.' && $entry != '..') {
209
+ $this->delTree($path.DS.$entry);
210
+ }
211
+ }
212
+ @rmdir($path);
213
+ } else {
214
+ @unlink($path);
215
+ }
216
+ return $this;
217
+ }
218
+
219
+ /**
220
+ * Run commands from Mage_Connect_Command
221
+ *
222
+ * @param string $command
223
+ * @param array $options
224
+ * @param array $params
225
+ * @return
226
+ */
227
+ public function run($command, $options=array(), $params=array())
228
+ {
229
+ @set_time_limit(0);
230
+ @ini_set('memory_limit', '256M');
231
+
232
+ if (empty($this->_cmdCache[$command])) {
233
+ Mage_Connect_Command::getCommands();
234
+ /**
235
+ * @var $cmd Mage_Connect_Command
236
+ */
237
+ $cmd = Mage_Connect_Command::getInstance($command);
238
+ if ($cmd instanceof Mage_Connect_Error) {
239
+ return $cmd;
240
+ }
241
+ $this->_cmdCache[$command] = $cmd;
242
+ } else {
243
+ /**
244
+ * @var $cmd Mage_Connect_Command
245
+ */
246
+ $cmd = $this->_cmdCache[$command];
247
+ }
248
+ $ftp=$this->getConfig()->remote_config;
249
+ if(strlen($ftp)>0){
250
+ $options=array_merge($options, array('ftp'=>$ftp));
251
+ }
252
+ $cmd->run($command, $options, $params);
253
+ if ($cmd->ui()->hasErrors()) {
254
+ return false;
255
+ } else {
256
+ return true;
257
+ }
258
+ }
259
+
260
+ public function setRemoteConfig($uri) #$host, $user, $password, $path='', $port=null)
261
+ {
262
+ #$uri = 'ftp://' . $user . ':' . $password . '@' . $host . (is_numeric($port) ? ':' . $port : '') . '/' . trim($path, '/') . '/';
263
+ //$this->run('config-set', array(), array('remote_config', $uri));
264
+ //$this->run('config-set', array('ftp'=>$uri), array('remote_config', $uri));
265
+ $this->getConfig()->remote_config=$uri;
266
+ return $this;
267
+ }
268
+
269
+ /**
270
+ *
271
+ * @param array $errors Error messages
272
+ * @return Maged_Connect
273
+ */
274
+ public function showConnectErrors($errors)
275
+ {
276
+ echo '<script type="text/javascript">';
277
+ $run = new Maged_Model_Connect_Request();
278
+ if ($callback = $run->get('failure_callback')) {
279
+ if (is_array($callback)) {
280
+ call_user_func_array($callback, array($result));
281
+ } else {
282
+ echo $callback;
283
+ }
284
+ }
285
+ echo '</script>';
286
+
287
+ return $this;
288
+ }
289
+
290
+ /**
291
+ * Run Mage_Connect_Command with html output console style
292
+ *
293
+ * @param array|Maged_Model $runParams command, options, params,
294
+ * comment, success_callback, failure_callback
295
+ */
296
+ public function runHtmlConsole($runParams)
297
+ {
298
+ if (function_exists('apache_setenv')) {
299
+ apache_setenv('no-gzip', '1');
300
+ }
301
+ @ini_set('zlib.output_compression', 0);
302
+ @ini_set('implicit_flush', 1);
303
+ for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
304
+ ob_implicit_flush();
305
+
306
+ $fe = $this->getFrontend();
307
+ $oldLogStream = $fe->getLogStream();
308
+ $fe->setLogStream('stdout');
309
+
310
+ if ($runParams instanceof Maged_Model) {
311
+ $run = $runParams;
312
+ } elseif (is_array($runParams)) {
313
+ $run = new Maged_Model_Connect_Request($runParams);
314
+ } elseif (is_string($runParams)) {
315
+ $run = new Maged_Model_Connect_Request(array('comment'=>$runParams));
316
+ } else {
317
+ throw Maged_Exception("Invalid run parameters");
318
+ }
319
+
320
+ if (!$run->get('no-header')) {
321
+ ?>
322
+ <html><head><style type="text/css">
323
+ body { margin:0px;
324
+ padding:3px;
325
+ background:black;
326
+ color:#2EC029;
327
+ font:normal 11px Lucida Console, Courier New, serif;
328
+ }
329
+ </style></head><body>
330
+ <script type="text/javascript">
331
+ if (parent && parent.disableInputs) {
332
+ parent.disableInputs(true);
333
+ }
334
+ if (typeof auto_scroll=='undefined') {
335
+ var auto_scroll = window.setInterval(console_scroll, 10);
336
+ }
337
+ function console_scroll()
338
+ {
339
+ if (typeof top.$ != 'function') {
340
+ return;
341
+ }
342
+ if (top.$('connect_iframe_scroll').checked) {
343
+ document.body.scrollTop+=3;
344
+ }
345
+ }
346
+ function show_message(message, newline)
347
+ {
348
+ var bodyElement = document.getElementsByTagName('body')[0];
349
+ if (typeof newline == 'undefined') {
350
+ newline = true
351
+ }
352
+ if (newline) {
353
+ bodyElement.innerHTML += '<br/>';
354
+ }
355
+ bodyElement.innerHTML += message;
356
+ }
357
+ function clear_cache(callbacks)
358
+ {
359
+ if (typeof top.Ajax != 'object') {
360
+ return;
361
+ }
362
+ var message = 'Exception during cache and session cleaning';
363
+ var url = window.location.href.split('?')[0] + '?A=cleanCache';
364
+ var intervalID = setInterval(function() {show_message('.', false); }, 500);
365
+ var clean = 0;
366
+ var maintenance = 0;
367
+ if (window.location.href.indexOf('clean_sessions') >= 0) {
368
+ clean = 1;
369
+ }
370
+ if (window.location.href.indexOf('maintenance') >= 0) {
371
+ maintenance = 1;
372
+ }
373
+
374
+ new top.Ajax.Request(url, {
375
+ method: 'post',
376
+ parameters: {clean_sessions:clean, maintenance:maintenance},
377
+ onCreate: function() {
378
+ show_message('Cleaning cache');
379
+ show_message('');
380
+ },
381
+ onSuccess: function(transport, json) {
382
+ var result = true;
383
+ try{
384
+ var response = eval('(' + transport.responseText + ')');
385
+ if (typeof response.result != 'undefined') {
386
+ result = response.result;
387
+ } else {
388
+ result = false;
389
+ }
390
+ if (typeof response.message != 'undefined') {
391
+ if (response.message.length > 0) {
392
+ message = response.message;
393
+ } else {
394
+ message = 'Cache cleaned successfully';
395
+ }
396
+ }
397
+ } catch (ex){
398
+ result = false;
399
+ }
400
+ if (result) {
401
+ callbacks.success();
402
+ } else {
403
+ callbacks.fail();
404
+ }
405
+ },
406
+ onFailure: function() {
407
+ callbacks.fail();
408
+ },
409
+ onComplete: function(transport) {
410
+ clearInterval(intervalID);
411
+ show_message(message);
412
+ }
413
+ });
414
+ }
415
+ </script>
416
+ <?php
417
+ }
418
+ echo htmlspecialchars($run->get('comment'));
419
+
420
+ if ($command = $run->get('command')) {
421
+ $result = $this->run($command, $run->get('options'), $run->get('params'));
422
+
423
+ if ($this->getFrontend()->hasErrors()) {
424
+ echo "<br/>CONNECT ERROR: ";
425
+ foreach ($this->getFrontend()->getErrors(false) as $error) {
426
+ echo nl2br($error[1]);
427
+ echo '<br/>';
428
+ }
429
+ }
430
+ echo '<script type="text/javascript">';
431
+ if ($this->getFrontend()->hasErrors()) {
432
+ if ($callback = $run->get('failure_callback')) {
433
+ if (is_array($callback)) {
434
+ call_user_func_array($callback, array($result));
435
+ } else {
436
+ echo $callback;
437
+ }
438
+ }
439
+ } else {
440
+ if (!$run->get('no-footer')) {
441
+ if ($callback = $run->get('success_callback')) {
442
+ if (is_array($callback)) {
443
+ call_user_func_array($callback, array($result));
444
+ } else {
445
+ echo $callback;
446
+ }
447
+ }
448
+ }
449
+ }
450
+ echo '</script>';
451
+ } else {
452
+ $result = false;
453
+ }
454
+ if ($this->getFrontend()->getErrors() || !$run->get('no-footer')) {
455
+ ?>
456
+ <script type="text/javascript">
457
+ if (parent && parent.disableInputs) {
458
+ parent.disableInputs(false);
459
+ }
460
+ </script>
461
+ </body></html>
462
+ <?php
463
+ $fe->setLogStream($oldLogStream);
464
+ }
465
+ return $result;
466
+ }
467
+ }
downloader/Maged/Connect/Frontend.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class frontend
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Connect_Frontend extends Mage_Connect_Frontend
36
+ {
37
+
38
+ /**
39
+ * Log stream or not
40
+ *
41
+ * @var string
42
+ */
43
+ protected $_logStream = null;
44
+
45
+ /**
46
+ * Output cache
47
+ *
48
+ * @var array
49
+ */
50
+ protected $_out = array();
51
+
52
+ /**
53
+ * Set log stream
54
+ *
55
+ * @param string|resource $stream 'stdout' or open php stream
56
+ */
57
+ public function setLogStream($stream)
58
+ {
59
+ $this->_logStream = $stream;
60
+ return $this;
61
+ }
62
+
63
+ /**
64
+ * Retrieve log stream
65
+ *
66
+ * @return string
67
+ */
68
+ public function getLogStream()
69
+ {
70
+ return $this->_logStream;
71
+ }
72
+
73
+ /**
74
+ * Echo data from executed command
75
+ */
76
+ public function output($data)
77
+ {
78
+
79
+ $this->_out = $data;
80
+
81
+ if ('stdout'===$this->_logStream) {
82
+ if (is_string($data)) {
83
+ echo $data."<br/>".str_repeat(" ", 256);
84
+ } elseif (is_array($data)) {
85
+ $data = array_pop($data);
86
+ if (!empty($data['message']) && is_string($data['message'])) {
87
+ echo $data['message']."<br/>".str_repeat(" ", 256);
88
+ } elseif (!empty($data['data'])) {
89
+ if (is_string($data['data'])) {
90
+ echo $data['data']."<br/>".str_repeat(" ", 256);
91
+ } else {
92
+ if (isset($data['title'])) {
93
+ echo $data['title']."<br/>".str_repeat(" ", 256);
94
+ }
95
+ if (is_array($data['data'])) {
96
+ foreach ($data['data'] as $row) {
97
+ foreach ($row as $msg) {
98
+ echo "&nbsp;".$msg;
99
+ }
100
+ echo "<br/>".str_repeat(" ", 256);
101
+ }
102
+ } else {
103
+ echo "&nbsp;".$data['data'];
104
+ }
105
+ }
106
+ }
107
+ } else {
108
+ print_r($data);
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Method for ask client about rewrite all files.
115
+ *
116
+ * @param $string
117
+ */
118
+ public function confirm($string)
119
+ {
120
+ $formId = $_POST['form_id'];
121
+ echo <<<SCRIPT
122
+ <script type="text/javascript">
123
+ if (confirm("{$string}")) {
124
+ parent.document.getElementById('ignore_local_modification').value=1;
125
+ parent.onSuccess();
126
+ if (parent && parent.disableInputs) {
127
+ parent.disableInputs(false);
128
+ }
129
+ window.onload = function () {
130
+ parent.document.getElementById('{$formId}').submit();
131
+ parent.document.getElementById('ignore_local_modification').value='';
132
+ }
133
+ }
134
+ </script>
135
+ SCRIPT;
136
+ }
137
+
138
+ /**
139
+ * Retrieve output cache
140
+ *
141
+ * @return array
142
+ */
143
+ public function getOutput()
144
+ {
145
+ return $this->_out;
146
+ }
147
+
148
+ }
149
+
downloader/Maged/Controller.php ADDED
@@ -0,0 +1,977 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class Controller
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ final class Maged_Controller
36
+ {
37
+ /**
38
+ * Request key of action
39
+ */
40
+ const ACTION_KEY = 'A';
41
+
42
+ /**
43
+ * Instance of class
44
+ *
45
+ * @var Maged_Controller
46
+ */
47
+ private static $_instance;
48
+
49
+ /**
50
+ * Current action name
51
+ *
52
+ * @var string
53
+ */
54
+ private $_action;
55
+
56
+ /**
57
+ * Controller is dispathed flag
58
+ *
59
+ * @var bool
60
+ */
61
+ private $_isDispatched = false;
62
+
63
+ /**
64
+ * Redirect to URL
65
+ *
66
+ * @var string
67
+ */
68
+ private $_redirectUrl;
69
+
70
+ /**
71
+ * Downloader dir path
72
+ *
73
+ * @var string
74
+ */
75
+ private $_rootDir;
76
+
77
+ /**
78
+ * Magento root dir path
79
+ *
80
+ * @var string
81
+ */
82
+ private $_mageDir;
83
+
84
+ /**
85
+ * View instance
86
+ *
87
+ * @var Maged_View
88
+ */
89
+ private $_view;
90
+
91
+ /**
92
+ * Connect config instance
93
+ *
94
+ * @var Mage_Connect_Config
95
+ */
96
+ private $_config;
97
+
98
+ /**
99
+ * Config instance
100
+ *
101
+ * @var Maged_Model_Config
102
+ */
103
+ private $_localConfig;
104
+
105
+ /**
106
+ * Session instance
107
+ *
108
+ * @var Maged_Model_Session
109
+ */
110
+ private $_session;
111
+
112
+ /**
113
+ * Root dir is writable flag
114
+ *
115
+ * @var bool
116
+ */
117
+ private $_writable;
118
+
119
+ /**
120
+ * Use maintenance flag
121
+ *
122
+ * @var bool
123
+ */
124
+ protected $_maintenance;
125
+
126
+ /**
127
+ * Maintenance file path
128
+ *
129
+ * @var string
130
+ */
131
+ protected $_maintenanceFile;
132
+
133
+ /**
134
+ * Register array for singletons
135
+ *
136
+ * @var array
137
+ */
138
+ protected $_singletons = array();
139
+
140
+ //////////////////////////// ACTIONS
141
+
142
+
143
+ /**
144
+ * Get ftp string from post data
145
+ *
146
+ * @param array $post post data
147
+ * @return string FTP Url
148
+ */
149
+ private function getFtpPost($post){
150
+ if (empty($post['ftp_host'])) {
151
+ $_POST['ftp'] = '';
152
+ return '';
153
+ }
154
+ $ftp = 'ftp://';
155
+ $post['ftp_proto'] = 'ftp://';
156
+
157
+ if (!empty($post['ftp_path']) && strlen(trim($post['ftp_path'], '\\/')) > 0) {
158
+ $post['ftp_path'] = '/' . trim($post['ftp_path'], '\\/') . '/';
159
+ } else {
160
+ $post['ftp_path'] = '/';
161
+ }
162
+
163
+ $start = stripos($post['ftp_host'],'ftp://');
164
+ if ($start !== false){
165
+ $post['ftp_proto'] = 'ftp://';
166
+ $post['ftp_host'] = substr($post['ftp_host'], $start + 6 - 1);
167
+ }
168
+ $start = stripos($post['ftp_host'],'ftps://');
169
+ if ($start !== false) {
170
+ $post['ftp_proto'] = 'ftps://';
171
+ $post['ftp_host'] = substr($post['ftp_host'], $start + 7 - 1);
172
+ }
173
+
174
+ $post['ftp_host'] = trim($post['ftp_host'], '\\/');
175
+
176
+ if (!empty($post['ftp_login']) && !empty($post['ftp_password'])){
177
+ $ftp = sprintf("%s%s:%s@%s%s",
178
+ $post['ftp_proto'],
179
+ $post['ftp_login'],
180
+ $post['ftp_password'],
181
+ $post['ftp_host'],
182
+ $post['ftp_path']
183
+ );
184
+ } elseif (!empty($post['ftp_login'])) {
185
+ $ftp = sprintf(
186
+ "%s%s@%s%s",
187
+ $post['ftp_proto'],
188
+ $post['ftp_login'],
189
+ $post['ftp_host'],
190
+ $post['ftp_path']
191
+ );
192
+ } else {
193
+ $ftp = $post['ftp_proto'] . $post['ftp_host'] . $post['ftp_path'];
194
+ }
195
+
196
+ $_POST['ftp'] = $ftp;
197
+ return $ftp;
198
+ }
199
+
200
+ /**
201
+ * NoRoute
202
+ */
203
+ public function norouteAction()
204
+ {
205
+ header("HTTP/1.0 404 Invalid Action");
206
+ echo $this->view()->template('noroute.phtml');
207
+ }
208
+
209
+ /**
210
+ * Login
211
+ */
212
+ public function loginAction()
213
+ {
214
+ $this->view()->set('username', !empty($_GET['username']) ? $_GET['username'] : '');
215
+ echo $this->view()->template('login.phtml');
216
+ }
217
+
218
+ /**
219
+ * Logout
220
+ */
221
+ public function logoutAction()
222
+ {
223
+ $this->session()->logout();
224
+ $this->redirect($this->url());
225
+ }
226
+
227
+ /**
228
+ * Index
229
+ */
230
+ public function indexAction()
231
+ {
232
+ $config = $this->config();
233
+ if (!$this->isInstalled()) {
234
+ $this->view()->set('mage_url', dirname(dirname($_SERVER['SCRIPT_NAME'])));
235
+ $this->view()->set(
236
+ 'use_custom_permissions_mode',
237
+ $config->__get('use_custom_permissions_mode')
238
+ ? $config->__get('use_custom_permissions_mode')
239
+ : '0'
240
+ );
241
+ $this->view()->set('mkdir_mode', decoct($config->__get('global_dir_mode')));
242
+ $this->view()->set('chmod_file_mode', decoct($config->__get('global_file_mode')));
243
+ $this->view()->set('protocol', $config->__get('protocol'));
244
+ $this->channelConfig()->setInstallView($config,$this->view());
245
+
246
+ echo $this->view()->template('install/download.phtml');
247
+ } elseif (!$config->sync_pear) {
248
+ $this->model('connect', true)->connect()->run('sync');
249
+ $this->forward('connectPackages');
250
+ } else {
251
+ $this->forward('connectPackages');
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Empty Action
257
+ */
258
+ public function emptyAction()
259
+ {
260
+ $this->model('connect', true)
261
+ ->connect()
262
+ ->runHtmlConsole('Please wait, preparing for updates...');
263
+ }
264
+
265
+ /**
266
+ * Install all magento
267
+ */
268
+ public function connectInstallAllAction()
269
+ {
270
+ $p = &$_POST;
271
+ $this->getFtpPost($p);
272
+ $errors = $this->model('connect', true)->validateConfigPost($p);
273
+ /* todo show errors */
274
+ if ($errors) {
275
+ $message = "CONNECT ERROR: ";
276
+ foreach ($errors as $err) {
277
+ $message .= $err . "\n";
278
+ }
279
+ $this->model('connect', true)->connect()->runHtmlConsole($message);
280
+ $this->model('connect', true)->connect()->showConnectErrors($errors);
281
+ return;
282
+ }
283
+
284
+ if( 1 == $p['inst_protocol']){
285
+ $this->model('connect', true)->connect()->setRemoteConfig($this->getFtpPost($p));
286
+ }
287
+
288
+ $this->channelConfig()->setPostData($this->config(),$p);
289
+
290
+ $chan = $this->config()->__get('root_channel');
291
+ $this->model('connect', true)->saveConfigPost($_POST);
292
+ $this->channelConfig()->setSettingsSession($_POST, $this->session());
293
+ $this->model('connect', true)->installAll(!empty($_GET['force']), $chan);
294
+ $p = null;
295
+ }
296
+
297
+ /**
298
+ * Connect packages
299
+ */
300
+ public function connectPackagesAction()
301
+ {
302
+ $connect = $this->model('connect', true);
303
+
304
+ if (isset($_GET['loggedin'])) {
305
+ $connect->connect()->run('sync');
306
+ }
307
+
308
+ $this->view()->set('connect', $connect);
309
+ $this->view()->set('channel_config', $this->channelConfig());
310
+ $remoteConfig = $this->config()->remote_config;
311
+ if (!$this->isWritable() && empty($remoteConfig)) {
312
+ $this->view()->set('writable_warning', true);
313
+ }
314
+
315
+ echo $this->view()->template('connect/packages.phtml');
316
+ }
317
+
318
+ /**
319
+ * Connect packages POST
320
+ */
321
+ public function connectPackagesPostAction()
322
+ {
323
+ $actions = isset($_POST['actions']) ? $_POST['actions'] : array();
324
+ if (isset($_POST['ignore_local_modification'])) {
325
+ $ignoreLocalModification = $_POST['ignore_local_modification'];
326
+ } else {
327
+ $ignoreLocalModification = '';
328
+ }
329
+ $this->model('connect', true)->applyPackagesActions($actions, $ignoreLocalModification);
330
+ }
331
+
332
+ /**
333
+ * Prepare package to install, get dependency info.
334
+ */
335
+ public function connectPreparePackagePostAction()
336
+ {
337
+ if (!$_POST) {
338
+ echo "INVALID POST DATA";
339
+ return;
340
+ }
341
+ $prepareResult = $this->model('connect', true)->prepareToInstall($_POST['install_package_id']);
342
+
343
+ $packages = isset($prepareResult['data']) ? $prepareResult['data'] : array();
344
+ $errors = isset($prepareResult['errors']) ? $prepareResult['errors'] : array();
345
+
346
+ $this->view()->set('packages', $packages);
347
+ $this->view()->set('errors', $errors);
348
+ $this->view()->set('package_id', $_POST['install_package_id']);
349
+
350
+ echo $this->view()->template('connect/packages_prepare.phtml');
351
+ }
352
+
353
+ /**
354
+ * Install package
355
+ */
356
+ public function connectInstallPackagePostAction()
357
+ {
358
+ if (!$_POST) {
359
+ echo "INVALID POST DATA";
360
+ return;
361
+ }
362
+ $this->model('connect', true)->installPackage($_POST['install_package_id']);
363
+ }
364
+
365
+ /**
366
+ * Install uploaded package
367
+ */
368
+ public function connectInstallPackageUploadAction()
369
+ {
370
+ if (!$_FILES) {
371
+ echo "No file was uploaded";
372
+ return;
373
+ }
374
+
375
+ if(empty($_FILES['file'])) {
376
+ echo "No file was uploaded";
377
+ return;
378
+ }
379
+
380
+ $info =& $_FILES['file'];
381
+
382
+ if(0 !== intval($info['error'])) {
383
+ echo "File upload problem";
384
+ return;
385
+ }
386
+
387
+ $target = $this->_mageDir . DS . "var/" . uniqid() . $info['name'];
388
+ $res = move_uploaded_file($info['tmp_name'], $target);
389
+ if(false === $res) {
390
+ echo "Error moving uploaded file";
391
+ return;
392
+ }
393
+
394
+ $this->model('connect', true)->installUploadedPackage($target);
395
+ @unlink($target);
396
+ }
397
+
398
+ /**
399
+ * Clean cache on ajax request
400
+ */
401
+ public function cleanCacheAction()
402
+ {
403
+ $result = $this->cleanCache();
404
+ echo json_encode($result);
405
+ }
406
+
407
+ /**
408
+ * Settings
409
+ */
410
+ public function settingsAction()
411
+ {
412
+ $config = $this->config();
413
+ $this->view()->set('preferred_state', $config->__get('preferred_state'));
414
+ $this->view()->set('protocol', $config->__get('protocol'));
415
+
416
+ $this->view()->set('use_custom_permissions_mode', $config->__get('use_custom_permissions_mode'));
417
+ $this->view()->set('mkdir_mode', decoct($config->__get('global_dir_mode')));
418
+ $this->view()->set('chmod_file_mode', decoct($config->__get('global_file_mode')));
419
+
420
+ $this->channelConfig()->setSettingsView($this->session(), $this->view());
421
+
422
+ $fs_disabled =! $this->isWritable();
423
+ $ftpParams = $config->__get('remote_config') ? @parse_url($config->__get('remote_config')) : '';
424
+
425
+ $this->view()->set('fs_disabled', $fs_disabled);
426
+ $this->view()->set('deployment_type', ($fs_disabled || !empty($ftpParams) ? 'ftp' : 'fs'));
427
+
428
+ if (!empty($ftpParams)) {
429
+ $this->view()->set('ftp_host', sprintf("%s://%s", $ftpParams['scheme'], $ftpParams['host']));
430
+ $this->view()->set('ftp_login', $ftpParams['user']);
431
+ $this->view()->set('ftp_password', $ftpParams['pass']);
432
+ $this->view()->set('ftp_path', $ftpParams['path']);
433
+ }
434
+ echo $this->view()->template('settings.phtml');
435
+ }
436
+
437
+ /**
438
+ * Settings post
439
+ */
440
+ public function settingsPostAction()
441
+ {
442
+ if ($_POST) {
443
+ $ftp = $this->getFtpPost($_POST);
444
+
445
+ /* clear startup messages */
446
+ $this->config();
447
+ $this->session()->getMessages();
448
+
449
+ $errors = $this->model('connect', true)->validateConfigPost($_POST);
450
+ if ($errors) {
451
+ foreach ($errors as $err) {
452
+ $this->session()->addMessage('error', $err);
453
+ }
454
+ $this->redirect($this->url('settings'));
455
+ return;
456
+ }
457
+ try {
458
+ if ('ftp' == $_POST['deployment_type'] && !empty($_POST['ftp_host'])) {
459
+ $this->model('connect', true)->connect()->setRemoteConfig($ftp);
460
+ } else {
461
+ $this->model('connect', true)->connect()->setRemoteConfig('');
462
+ $_POST['ftp'] = '';
463
+ }
464
+ $this->channelConfig()->setPostData($this->config(), $_POST);
465
+ $this->model('connect', true)->saveConfigPost($_POST);
466
+ $this->channelConfig()->setSettingsSession($_POST, $this->session());
467
+ $this->model('connect', true)->connect()->run('sync');
468
+ } catch (Exception $e) {
469
+ $this->session()->addMessage('error', "Unable to save settings: " . $e->getMessage());
470
+ }
471
+ }
472
+ $this->redirect($this->url('settings'));
473
+ }
474
+
475
+ //////////////////////////// ABSTRACT
476
+
477
+ /**
478
+ * Constructor
479
+ */
480
+ public function __construct()
481
+ {
482
+ $this->_rootDir = dirname(dirname(__FILE__));
483
+ $this->_mageDir = dirname($this->_rootDir);
484
+ }
485
+
486
+ /**
487
+ * Run
488
+ */
489
+ public static function run()
490
+ {
491
+ try {
492
+ self::singleton()->dispatch();
493
+ } catch (Exception $e) {
494
+ echo $e->getMessage();
495
+ //echo self::singleton()->view()->set('exception', $e)->template("exception.phtml");
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Initialize object of class
501
+ *
502
+ * @return Maged_Controller
503
+ */
504
+ public static function singleton()
505
+ {
506
+ if (!self::$_instance) {
507
+ self::$_instance = new self;
508
+
509
+ if (self::$_instance->isDownloaded() && self::$_instance->isInstalled()) {
510
+ Mage::app('', 'store', array('global_ban_use_cache'=>true));
511
+ Mage::getSingleton('adminhtml/url')->turnOffSecretKey();
512
+ }
513
+ }
514
+ return self::$_instance;
515
+ }
516
+
517
+ /**
518
+ * Retrieve Downloader root dir
519
+ *
520
+ * @return string
521
+ */
522
+ public function getRootDir()
523
+ {
524
+ return $this->_rootDir;
525
+ }
526
+
527
+ /**
528
+ * Retrieve Magento root dir
529
+ *
530
+ * @return string
531
+ */
532
+ public function getMageDir()
533
+ {
534
+ return $this->_mageDir;
535
+ }
536
+
537
+ /**
538
+ * Retrieve Mage Class file path
539
+ *
540
+ * @return string
541
+ */
542
+ public function getMageFilename()
543
+ {
544
+ $ds = DIRECTORY_SEPARATOR;
545
+ return $this->getMageDir() . $ds . 'app' . $ds . 'Mage.php';
546
+ }
547
+
548
+ /**
549
+ * Retrieve path for Varien_Profiler
550
+ *
551
+ * @return string
552
+ */
553
+ public function getVarFilename()
554
+ {
555
+ $ds = DIRECTORY_SEPARATOR;
556
+ return $this->getMageDir() . $ds . 'lib' . $ds . 'Varien' . $ds . 'Profiler.php';
557
+ }
558
+
559
+ /**
560
+ * Retrieve downloader file path
561
+ *
562
+ * @param string $name
563
+ * @return string
564
+ */
565
+ public function filepath($name = '')
566
+ {
567
+ $ds = DIRECTORY_SEPARATOR;
568
+ return rtrim($this->getRootDir() . $ds . str_replace('/', $ds, $name), $ds);
569
+ }
570
+
571
+ /**
572
+ * Retrieve object of view
573
+ *
574
+ * @return Maged_View
575
+ */
576
+ public function view()
577
+ {
578
+ if (!$this->_view) {
579
+ $this->_view = new Maged_View;
580
+ }
581
+ return $this->_view;
582
+ }
583
+
584
+ /**
585
+ * Retrieve object of model
586
+ *
587
+ * @param string $model
588
+ * @param boolean $singleton
589
+ * @return Maged_Model
590
+ */
591
+ public function model($model = null, $singleton = false)
592
+ {
593
+ if ($singleton && isset($this->_singletons[$model])) {
594
+ return $this->_singletons[$model];
595
+ }
596
+
597
+ if (is_null($model)) {
598
+ $class = 'Maged_Model';
599
+ } else {
600
+ $class = 'Maged_Model_'.str_replace(' ', '_', ucwords(str_replace('_', ' ', $model)));
601
+ if (!class_exists($class, false)) {
602
+ include_once str_replace('_', DIRECTORY_SEPARATOR, $class).'.php';
603
+ }
604
+ }
605
+
606
+ $object = new $class();
607
+
608
+ if ($singleton) {
609
+ $this->_singletons[$model] = $object;
610
+ }
611
+
612
+ return $object;
613
+ }
614
+
615
+ /**
616
+ * Retrieve object of config
617
+ *
618
+ * @return Mage_Connect_Config
619
+ */
620
+ public function config()
621
+ {
622
+ if (!$this->_config) {
623
+ $this->_config = $this->model('connect', true)->connect()->getConfig();
624
+ if (!$this->_config->isLoaded()) {
625
+ $this->session()->addMessage('error', "Settings has not been loaded. Used default settings");
626
+ if ($this->_config->getError()) {
627
+ $this->session()->addMessage('error', $this->_config->getError());
628
+ }
629
+ }
630
+ }
631
+ return $this->_config;
632
+ }
633
+
634
+ /**
635
+ * Retrieve object of channel config
636
+ *
637
+ * @return Maged_Model_Config_Interface
638
+ */
639
+ public function channelConfig()
640
+ {
641
+ if (!$this->_localConfig) {
642
+ $this->_localConfig = $this->model('config', true)->getChannelConfig();
643
+ }
644
+ return $this->_localConfig;
645
+ }
646
+
647
+ /**
648
+ * Retrieve object of session
649
+ *
650
+ * @return Maged_Model_Session
651
+ */
652
+ public function session()
653
+ {
654
+ if (!$this->_session) {
655
+ $this->_session = $this->model('session')->start();
656
+ }
657
+ return $this->_session;
658
+ }
659
+
660
+ /**
661
+ * Set Controller action
662
+ *
663
+ * @param string $action
664
+ * @return Maged_Controller
665
+ */
666
+ public function setAction($action=null)
667
+ {
668
+ if (is_null($action)) {
669
+ if (!empty($this->_action)) {
670
+ return $this;
671
+ }
672
+ $action = !empty($_GET[self::ACTION_KEY]) ? $_GET[self::ACTION_KEY] : 'index';
673
+ }
674
+ if (empty($action) || !is_string($action) || !method_exists($this, $this->getActionMethod($action))) {
675
+ //$action = 'noroute';
676
+ $action = 'index';
677
+ }
678
+ $this->_action = $action;
679
+ return $this;
680
+ }
681
+
682
+ /**
683
+ * Retrieve Controller action name
684
+ *
685
+ * @return string
686
+ */
687
+ public function getAction()
688
+ {
689
+ return $this->_action;
690
+ }
691
+
692
+ /**
693
+ * Set Redirect to URL
694
+ *
695
+ * @param string $url
696
+ * @param bool $force
697
+ * @return Maged_Controller
698
+ */
699
+ public function redirect($url, $force = false)
700
+ {
701
+ $this->_redirectUrl = $url;
702
+ if ($force) {
703
+ $this->processRedirect();
704
+ }
705
+ return $this;
706
+ }
707
+
708
+ /**
709
+ * Precess redirect
710
+ *
711
+ * @return Maged_Controller
712
+ */
713
+ public function processRedirect()
714
+ {
715
+ if ($this->_redirectUrl) {
716
+ if (headers_sent()) {
717
+ echo '<script type="text/javascript">location.href="' . $this->_redirectUrl . '"</script>';
718
+ exit;
719
+ } else {
720
+ header("Location: " . $this->_redirectUrl);
721
+ exit;
722
+ }
723
+ }
724
+ return $this;
725
+ }
726
+
727
+ /**
728
+ * Forward to action
729
+ *
730
+ * @param string $action
731
+ * @return Maged_Controller
732
+ */
733
+ public function forward($action)
734
+ {
735
+ $this->setAction($action);
736
+ $this->_isDispatched = false;
737
+ return $this;
738
+ }
739
+
740
+ /**
741
+ * Retrieve action method by action name
742
+ *
743
+ * @param string $action
744
+ * @return string
745
+ */
746
+ public function getActionMethod($action = null)
747
+ {
748
+ $method = (!is_null($action) ? $action : $this->_action) . 'Action';
749
+ return $method;
750
+ }
751
+
752
+ /**
753
+ * Generate URL for action
754
+ *
755
+ * @param string $action
756
+ * @param array $params
757
+ */
758
+ public function url($action = '', $params = array())
759
+ {
760
+ $args = array();
761
+ foreach ($params as $k => $v) {
762
+ $args[] = sprintf('%s=%s', rawurlencode($k), rawurlencode($v));
763
+ }
764
+ $args = $args ? join('&', $args) : '';
765
+
766
+ return sprintf('%s?%s=%s%s', $_SERVER['SCRIPT_NAME'], self::ACTION_KEY, rawurlencode($action), $args);
767
+ }
768
+
769
+ /**
770
+ * Dispatch process
771
+ */
772
+ public function dispatch()
773
+ {
774
+ header('Content-type: text/html; charset=UTF-8');
775
+
776
+ $this->setAction();
777
+
778
+ if (!$this->isInstalled()) {
779
+ if (!in_array($this->getAction(), array('index', 'connectInstallAll', 'empty', 'cleanCache'))) {
780
+ $this->setAction('index');
781
+ }
782
+ } else {
783
+ $this->session()->authenticate();
784
+ }
785
+
786
+ while (!$this->_isDispatched) {
787
+ $this->_isDispatched = true;
788
+
789
+ $method = $this->getActionMethod();
790
+ $this->$method();
791
+ }
792
+
793
+ $this->processRedirect();
794
+ }
795
+
796
+ /**
797
+ * Check root dir is writable
798
+ *
799
+ * @return bool
800
+ */
801
+ public function isWritable()
802
+ {
803
+ if (is_null($this->_writable)) {
804
+ $this->_writable = is_writable($this->getMageDir() . DIRECTORY_SEPARATOR)
805
+ && is_writable($this->filepath())
806
+ && (!file_exists($this->filepath('config.ini') || is_writable($this->filepath('config.ini'))));
807
+ }
808
+ return $this->_writable;
809
+ }
810
+
811
+ /**
812
+ * Check is Magento files downloaded
813
+ *
814
+ * @return bool
815
+ */
816
+ public function isDownloaded()
817
+ {
818
+ return file_exists($this->getMageFilename()) && file_exists($this->getVarFilename());
819
+ }
820
+
821
+ /**
822
+ * Check is Magento installed
823
+ *
824
+ * @return bool
825
+ */
826
+ public function isInstalled()
827
+ {
828
+ if (!$this->isDownloaded()) {
829
+ return false;
830
+ }
831
+ if (!class_exists('Mage', false)) {
832
+ if (!file_exists($this->getMageFilename())) {
833
+ return false;
834
+ }
835
+ include_once $this->getMageFilename();
836
+ Mage::setIsDownloader();
837
+ }
838
+ return Mage::isInstalled();
839
+ }
840
+
841
+ /**
842
+ * Retrieve Maintenance flag
843
+ *
844
+ * @return bool
845
+ */
846
+ protected function _getMaintenanceFlag()
847
+ {
848
+ if (is_null($this->_maintenance)) {
849
+ $this->_maintenance = !empty($_REQUEST['maintenance']) && $_REQUEST['maintenance'] == '1' ? true : false;
850
+ }
851
+ return $this->_maintenance;
852
+ }
853
+
854
+ /**
855
+ * Retrieve Maintenance Flag file path
856
+ *
857
+ * @return string
858
+ */
859
+ protected function _getMaintenanceFilePath()
860
+ {
861
+ if (is_null($this->_maintenanceFile)) {
862
+ $path = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR;
863
+ $this->_maintenanceFile = $path . 'maintenance.flag';
864
+ }
865
+ return $this->_maintenanceFile;
866
+ }
867
+
868
+ /**
869
+ * Begin install package(s)
870
+ */
871
+ public function startInstall()
872
+ {
873
+ if ($this->_getMaintenanceFlag()) {
874
+ $maintenance_filename='maintenance.flag';
875
+ $config = $this->config();
876
+ if (!$this->isWritable() || strlen($config->__get('remote_config')) > 0) {
877
+ $ftpObj = new Mage_Connect_Ftp();
878
+ $ftpObj->connect($config->__get('remote_config'));
879
+ $tempFile = tempnam(sys_get_temp_dir(),'maintenance');
880
+ @file_put_contents($tempFile, 'maintenance');
881
+ $ftpObj->upload($maintenance_filename, $tempFile);
882
+ $ftpObj->close();
883
+ } else {
884
+ @file_put_contents($this->_getMaintenanceFilePath(), 'maintenance');
885
+ }
886
+ }
887
+ }
888
+
889
+ /**
890
+ * End install package(s)
891
+ */
892
+ public function endInstall()
893
+ {
894
+ //$connect
895
+ /** @var $connect Maged_Model_Connect */
896
+ $frontend = $this->model('connect', true)->connect()->getFrontend();
897
+ if (!($frontend instanceof Maged_Connect_Frontend)) {
898
+ $this->cleanCache();
899
+ }
900
+ }
901
+
902
+ protected function cleanCache()
903
+ {
904
+ $result = true;
905
+ $message = '';
906
+ try {
907
+ if ($this->isInstalled()) {
908
+ if (!empty($_REQUEST['clean_sessions'])) {
909
+ Mage::app()->cleanAllSessions();
910
+ $message .= 'Session cleaned successfully. ';
911
+ }
912
+ Mage::app()->cleanCache();
913
+
914
+ // reinit config and apply all updates
915
+ Mage::app()->getConfig()->reinit();
916
+ Mage_Core_Model_Resource_Setup::applyAllUpdates();
917
+ Mage_Core_Model_Resource_Setup::applyAllDataUpdates();
918
+ $message .= 'Cache cleaned successfully';
919
+ } else {
920
+ $result = true;
921
+ }
922
+ } catch (Exception $e) {
923
+ $result = false;
924
+ $message = "Exception during cache and session cleaning: ".$e->getMessage();
925
+ $this->session()->addMessage('error', $message);
926
+ }
927
+
928
+ if ($result && $this->_getMaintenanceFlag()) {
929
+ $maintenance_filename='maintenance.flag';
930
+ $config = $this->config();
931
+ if (!$this->isWritable() && strlen($config->__get('remote_config')) > 0) {
932
+ $ftpObj = new Mage_Connect_Ftp();
933
+ $ftpObj->connect($config->__get('remote_config'));
934
+ $ftpObj->delete($maintenance_filename);
935
+ $ftpObj->close();
936
+ } else {
937
+ @unlink($this->_getMaintenanceFilePath());
938
+ }
939
+ }
940
+ return array('result' => $result, 'message' => $message);
941
+ }
942
+
943
+ /**
944
+ * Gets the current Magento Connect Manager (Downloader) version string
945
+ * @link http://www.magentocommerce.com/blog/new-community-edition-release-process/
946
+ *
947
+ * @return string
948
+ */
949
+ public static function getVersion()
950
+ {
951
+ $i = self::getVersionInfo();
952
+ return trim(
953
+ "{$i['major']}.{$i['minor']}.{$i['revision']}"
954
+ . ($i['patch'] != '' ? ".{$i['patch']}" : "")
955
+ . "-{$i['stability']}{$i['number']}",
956
+ '.-'
957
+ );
958
+ }
959
+
960
+ /**
961
+ * Gets the detailed Magento Connect Manager (Downloader) version information
962
+ * @link http://www.magentocommerce.com/blog/new-community-edition-release-process/
963
+ *
964
+ * @return array
965
+ */
966
+ public static function getVersionInfo()
967
+ {
968
+ return array(
969
+ 'major' => '1',
970
+ 'minor' => '5',
971
+ 'revision' => '0',
972
+ 'patch' => '0',
973
+ 'stability' => '',
974
+ 'number' => '',
975
+ );
976
+ }
977
+ }
downloader/Maged/Exception.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class Exception
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Exception extends Exception
36
+ {
37
+ }
downloader/Maged/Model.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class Model
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Model
36
+ {
37
+
38
+ /**
39
+ * Internal cache
40
+ *
41
+ * @var array
42
+ */
43
+ protected $_data;
44
+
45
+ /**
46
+ * Constructor
47
+ */
48
+ public function __construct()
49
+ {
50
+ $args = func_get_args();
51
+ if (empty($args[0])) {
52
+ $args[0] = array();
53
+ }
54
+ $this->_data = $args[0];
55
+
56
+ $this->_construct();
57
+ }
58
+
59
+ /**
60
+ * Constructor for covering
61
+ */
62
+ protected function _construct()
63
+ {
64
+
65
+ }
66
+
67
+ /**
68
+ * Retrieve controller
69
+ * @return Maged_Controller
70
+ */
71
+ public function controller()
72
+ {
73
+ return Maged_Controller::singleton();
74
+ }
75
+
76
+ /**
77
+ * Set value for key
78
+ *
79
+ * @param string $key
80
+ * @param mixed $value
81
+ * @return Maged_Model
82
+ */
83
+ public function set($key, $value)
84
+ {
85
+ $this->_data[$key] = $value;
86
+ return $this;
87
+ }
88
+
89
+ /**
90
+ * Get value by key
91
+ *
92
+ * @param string $key
93
+ * @return mixed
94
+ */
95
+ public function get($key)
96
+ {
97
+ return isset($this->_data[$key]) ? $this->_data[$key] : null;
98
+ }
99
+ }
downloader/Maged/Model/Config.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class config
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Model_Config extends Maged_Model_Config_Abstract
36
+ {
37
+ /**
38
+ * Get channel config class
39
+ * @return Maged_Model_Config_Interface
40
+ */
41
+ public function getChannelConfig()
42
+ {
43
+ $this->load();
44
+ $channel = trim($this->get('root_channel'));
45
+ if (!empty($channel)) {
46
+ try {
47
+ return $this->controller()->model('config_'.$channel, true);
48
+ } catch (Exception $e) {
49
+ throw new Exception('Not valid config.ini file.');
50
+ }
51
+ } else {
52
+ throw new Exception('Not valid config.ini file.');
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Save post data to config
58
+ *
59
+ * @param array $p
60
+ * @return Maged_Model_Config
61
+ */
62
+ public function saveConfigPost($p)
63
+ {
64
+ $configParams = array(
65
+ 'protocol',
66
+ 'preferred_state',
67
+ 'use_custom_permissions_mode',
68
+ 'mkdir_mode',
69
+ 'chmod_file_mode',
70
+ 'magento_root',
71
+ 'downloader_path',
72
+ 'root_channel_uri',
73
+ 'root_channel',
74
+ 'ftp',
75
+ );
76
+ foreach ($configParams as $paramName){
77
+ if (isset($p[$paramName])) {
78
+ $this->set($paramName, $p[$paramName]);
79
+ }
80
+ }
81
+ $this->save();
82
+ return $this;
83
+ }
84
+ }
downloader/Maged/Model/Config/Abstract.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class config
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Model_Config_Abstract extends Maged_Model
36
+ {
37
+ /**
38
+ * Retrive file name
39
+ *
40
+ * @return string
41
+ */
42
+ public function getFilename()
43
+ {
44
+ return $this->controller()->filepath('config.ini');
45
+ }
46
+
47
+ /**
48
+ * Load file
49
+ *
50
+ * @return Maged_Model_Config
51
+ */
52
+ public function load()
53
+ {
54
+ if (!file_exists($this->getFilename())) {
55
+ return $this;
56
+ }
57
+ $rows = file($this->getFilename());
58
+ if (!$rows) {
59
+ return $this;
60
+ }
61
+ foreach ($rows as $row) {
62
+ $arr = explode('=', $row, 2);
63
+ if (count($arr)!==2) {
64
+ continue;
65
+ }
66
+ $key = trim($arr[0]);
67
+ $value = trim($arr[1], " \t\"'\n\r");
68
+ if (!$key || $key[0]=='#' || $key[0]==';') {
69
+ continue;
70
+ }
71
+ $this->set($key, $value);
72
+ }
73
+ return $this;
74
+ }
75
+
76
+ /**
77
+ * Save file
78
+ *
79
+ * @return Maged_Model_Config
80
+ */
81
+ public function save()
82
+ {
83
+ if ((!is_writable($this->getFilename())&&is_file($this->getFilename()))||(dirname($this->getFilename())!=''&&!is_writable(dirname($this->getFilename())))) {
84
+ if(isset($this->_data['ftp'])&&!empty($this->_data['ftp'])&&strlen($this->get('downloader_path'))>0){
85
+ $confFile=$this->get('downloader_path').DIRECTORY_SEPARATOR.basename($this->getFilename());
86
+ $ftpObj = new Mage_Connect_Ftp();
87
+ $ftpObj->connect($this->_data['ftp']);
88
+ $tempFile = tempnam(sys_get_temp_dir(),'configini');
89
+ $fp = fopen($tempFile, 'w');
90
+ foreach ($this->_data as $k=>$v) {
91
+ fwrite($fp, $k.'='.$v."\n");
92
+ }
93
+ fclose($fp);
94
+ $ret=$ftpObj->upload($confFile, $tempFile);
95
+ $ftpObj->close();
96
+ }else{
97
+ /* @TODO: show Warning message*/
98
+ $this->controller()->session()
99
+ ->addMessage('warning', 'Invalid file permissions, could not save configuration.');
100
+ return $this;
101
+ }
102
+ /**/
103
+ }else{
104
+ $fp = fopen($this->getFilename(), 'w');
105
+ foreach ($this->_data as $k=>$v) {
106
+ fwrite($fp, $k.'='.$v."\n");
107
+ }
108
+ fclose($fp);
109
+ }
110
+ return $this;
111
+ }
112
+
113
+ /**
114
+ * Return channel label for channel name
115
+ *
116
+ * @param string $channel
117
+ * @return string
118
+ */
119
+ public function getChannelLabel($channel)
120
+ {
121
+ $channelLabel = '';
122
+ switch($channel)
123
+ {
124
+ case 'community':
125
+ $channelLabel = 'Magento Community Edition';
126
+ break;
127
+ default:
128
+ $channelLabel = $channel;
129
+ break;
130
+ }
131
+ return $channelLabel;
132
+ }
133
+ }
134
+ ?>
downloader/Maged/Model/Config/Community.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class config
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Model_Config_Community extends Maged_Model_Config_Abstract implements Maged_Model_Config_Interface
36
+ {
37
+
38
+ /**
39
+ * Initialization
40
+ */
41
+ protected function _construct()
42
+ {
43
+ $this->load();
44
+ }
45
+
46
+ /**
47
+ * Set data for Settings View
48
+ *
49
+ * @param Mage_Connect_Config $config
50
+ * @param Maged_View $view
51
+ * @return null
52
+ */
53
+ public function setInstallView($config, $view)
54
+ {
55
+ $view->set('channel_logo', 'logo');
56
+ }
57
+
58
+ /**
59
+ * Set data for Settings View
60
+ * @param Mage_Connect_Config $config
61
+ * @param Maged_View $view
62
+ * @return null
63
+ */
64
+ public function setSettingsView($config, $view)
65
+ {
66
+ }
67
+
68
+ /**
69
+ * Set session data for Settings
70
+ * @param array $post post data
71
+ * @param mixed $session Session object
72
+ * @return null
73
+ */
74
+ public function setSettingsSession($post, $session)
75
+ {
76
+ }
77
+
78
+ /**
79
+ * Get root channel URI
80
+ *
81
+ * @return string Root channel URI
82
+ */
83
+ public function getRootChannelUri(){
84
+ if (!$this->get('root_channel_uri')) {
85
+ $this->set('root_channel_uri', 'connect20.magentocommerce.com/community');
86
+ }
87
+ return $this->get('root_channel_uri');
88
+ }
89
+
90
+ /**
91
+ * Set config data from POST
92
+ *
93
+ * @param Mage_Connect_Config $config Config object
94
+ * @param array $post post data
95
+ * @return null
96
+ */
97
+ public function setPostData($config, &$post)
98
+ {
99
+ }
100
+
101
+ /**
102
+ * Set additional command options
103
+ *
104
+ * @param mixed $session Session object
105
+ * @param array $options
106
+ * @return null
107
+ */
108
+ public function setCommandOptions($session, &$options)
109
+ {
110
+ }
111
+ }
112
+ ?>
downloader/Maged/Model/Config/Interface.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class config
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ interface Maged_Model_Config_Interface
36
+ {
37
+
38
+ /**
39
+ * Set data for Settings View
40
+ *
41
+ * @param Mage_Connect_Config $config
42
+ * @param Maged_View $view
43
+ * @return null
44
+ */
45
+ public function setInstallView($config, $view);
46
+
47
+ /**
48
+ * Set data for Settings View
49
+ *
50
+ * @param mixed $session Session object
51
+ * @param Maged_View $view
52
+ * @return null
53
+ */
54
+ public function setSettingsView($session, $view);
55
+
56
+ /**
57
+ * Set session data for Settings
58
+ *
59
+ * @param array $post post data
60
+ * @param mixed $session Session object
61
+ * @return null
62
+ */
63
+ public function setSettingsSession($post, $session);
64
+
65
+ /**
66
+ * Set config data from POST
67
+ *
68
+ * @param Mage_Connect_Config $config Config object
69
+ * @param array $post post data
70
+ * @return boolean
71
+ */
72
+ public function setPostData($config, &$post);
73
+
74
+ /**
75
+ * Get root channel URI
76
+ *
77
+ * @return string Root channel URI
78
+ */
79
+ public function getRootChannelUri();
80
+
81
+ /**
82
+ * Set additional command options
83
+ *
84
+ * @param mixed $session Session object
85
+ * @param array $options
86
+ * @return null
87
+ */
88
+ public function setCommandOptions($session, &$options);
89
+ }
90
+ ?>
downloader/Maged/Model/Connect.php ADDED
@@ -0,0 +1,491 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ include_once "Maged/Connect.php";
28
+
29
+ /**
30
+ * Class for initialize Mage_Connect lib
31
+ *
32
+ * @category Mage
33
+ * @package Mage_Connect
34
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
35
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
36
+ */
37
+
38
+ class Maged_Model_Connect extends Maged_Model
39
+ {
40
+
41
+ /**
42
+ * Constructor
43
+ */
44
+ protected function _construct()
45
+ {
46
+ parent::_construct();
47
+ }
48
+
49
+ /**
50
+ * Retrive object of Maged_Connect
51
+ *
52
+ * @return Maged_Connect
53
+ */
54
+ public function connect()
55
+ {
56
+ return Maged_Connect::getInstance();
57
+ }
58
+
59
+ /**
60
+ * Install All Magento
61
+ *
62
+ * @param boolean $force
63
+ */
64
+ public function installAll($force=false, $chanName='')
65
+ {
66
+ $options = array('install_all'=>true);
67
+ if ($force) {
68
+ $this->connect()->cleanSconfig();
69
+ $options['force'] = 1;
70
+ }
71
+ $packages = array(
72
+ 'Mage_All_Latest',
73
+ );
74
+ $connectConfig = $this->connect()->getConfig();
75
+ $ftp = $connectConfig->remote_config;
76
+ if (!empty($ftp)) {
77
+ $options['ftp'] = $ftp;
78
+ }
79
+ $params = array();
80
+
81
+ $uri = $this->controller()->channelConfig()->getRootChannelUri();
82
+
83
+ $this->controller()->channelConfig()->setCommandOptions($this->controller()->session(), $options);
84
+
85
+ $connectConfig->root_channel = $chanName;
86
+ foreach ($packages as $package) {
87
+ $params[] = $uri;
88
+ $params[] = $package;
89
+ }
90
+ $this->connect()->runHtmlConsole(array('command'=>'install', 'options'=>$options, 'params'=>$params));
91
+ }
92
+
93
+ /**
94
+ * Prepare to install package
95
+ *
96
+ * @param string $id
97
+ * @return array
98
+ */
99
+ public function prepareToInstall($id)
100
+ {
101
+ $match = array();
102
+ if (!$this->checkExtensionKey($id, $match)) {
103
+ echo('Invalid package identifier provided: '.$id);
104
+ exit;
105
+ }
106
+
107
+ $channel = $match[1];
108
+ $package = $match[2];
109
+ $version = (!empty($match[3]) ? trim($match[3],'/\-') : '');
110
+
111
+ $connect = $this->connect();
112
+ $sconfig = $connect->getSingleConfig();
113
+
114
+ $options = array();
115
+ $params = array($channel, $package, $version, $version);
116
+ $this->controller()->channelConfig()->setCommandOptions($this->controller()->session(), $options);
117
+
118
+ $connect->run('package-prepare', $options, $params);
119
+ $output = $connect->getOutput();
120
+ $errors = $connect->getFrontend()->getErrors();
121
+ $package_error = array();
122
+ foreach ($errors as $error){
123
+ if (isset($error[1])){
124
+ $package_error[] = $error[1];
125
+ }
126
+ }
127
+
128
+ $packages = array();
129
+ if (is_array($output) && isset($output['package-prepare'])){
130
+ $packages = array_merge($output['package-prepare'], array('errors'=>array('error'=>$package_error)));
131
+ } elseif (is_array($output) && !empty($package_error)) {
132
+ $packages = array('errors'=>array('error'=>$package_error));
133
+ }
134
+ return $packages;
135
+ }
136
+
137
+
138
+ /**
139
+ * Retrieve all installed packages
140
+ *
141
+ * @return array
142
+ */
143
+ public function getAllInstalledPackages()
144
+ {
145
+ $connect = $this->connect();
146
+ $sconfig = $connect->getSingleConfig(true);
147
+ $connect->run('list-installed');
148
+ $output = $connect->getOutput();
149
+ $packages = array();
150
+ if (is_array($output) && isset($output['list-installed']['data'])){
151
+ $packages = $output['list-installed']['data'];
152
+ } else {
153
+
154
+ }
155
+ foreach ($packages as $channel=>$package) {
156
+ foreach ($package as $name=>$data) {
157
+ $summary = $sconfig->getPackageObject($channel, $name)->getSummary();
158
+ $addition = array('summary'=>$summary, 'upgrade_versions'=>array(), 'upgrade_latest'=>'');
159
+ $packages[$channel][$name] = array_merge($data, $addition);
160
+ }
161
+ }
162
+
163
+ if (!empty($_GET['updates'])) {
164
+ $options = array();
165
+ $this->controller()->channelConfig()->setCommandOptions($this->controller()->session(), $options);
166
+ $result = $connect->run('list-upgrades', $options);
167
+ $output = $connect->getOutput();
168
+ if (is_array($output)) {
169
+ $channelData = $output;
170
+ if (!empty($channelData['list-upgrades']['data']) && is_array($channelData['list-upgrades']['data'])) {
171
+ foreach ($channelData['list-upgrades']['data'] as $channel=>$package) {
172
+ foreach ($package as $name=>$data) {
173
+ if (!isset($packages[$channel][$name])) {
174
+ continue;
175
+ }
176
+ $packages[$channel][$name]['upgrade_latest'] = $data['to'].' ('.$data['from'].')';
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ $states = array('snapshot'=>0, 'devel'=>1, 'alpha'=>2, 'beta'=>3, 'stable'=>4);
184
+ $preferredState = $states[$this->getPreferredState()];
185
+
186
+ foreach ($packages as $channel=>&$package) {
187
+ foreach ($package as $name=>&$data) {
188
+ $actions = array();
189
+ $systemPkg = $name==='Mage_Downloader';
190
+ if (!empty($data['upgrade_latest'])) {
191
+ $status = 'upgrade-available';
192
+ $releases = array();
193
+ $connect->run('info', array(), array($channel, $name));
194
+ $output = $connect->getOutput();
195
+ if (!empty($output['info']['releases'])) {
196
+ foreach ($output['info']['releases'] as $release) {
197
+ $stability = $packages[$channel][$name]['stability'];
198
+ if ($states[$release['s']] < min($preferredState, $states[$stability])) {
199
+ continue;
200
+ }
201
+ if (version_compare($release['v'], $packages[$channel][$name]['version']) < 1) {
202
+ continue;
203
+ }
204
+ $releases[$release['v']] = $release['v'].' ('.$release['s'].')';
205
+ }
206
+ }
207
+
208
+ if ($releases) {
209
+ uksort($releases, 'version_compare');
210
+ foreach ($releases as $version => $release) {
211
+ $actions['upgrade|'.$version] = 'Upgrade to '.$release;
212
+ }
213
+ } else {
214
+ $a = explode(' ', $data['upgrade_latest'], 2);
215
+ $actions['upgrade|'.$a[0]] = 'Upgrade';
216
+ }
217
+ if (!$systemPkg) {
218
+ $actions['uninstall'] = 'Uninstall';
219
+ }
220
+ } else {
221
+ $status = 'installed';
222
+ $actions['reinstall'] = 'Reinstall';
223
+ if (!$systemPkg) {
224
+ $actions['uninstall'] = 'Uninstall';
225
+ }
226
+ }
227
+ $packages[$channel][$name]['actions'] = $actions;
228
+ $packages[$channel][$name]['status'] = $status;
229
+ }
230
+ }
231
+ return $packages;
232
+ }
233
+
234
+ /**
235
+ * Run packages action
236
+ *
237
+ * @param mixed $packages
238
+ */
239
+ public function applyPackagesActions($packages, $ignoreLocalModification='')
240
+ {
241
+ $actions = array();
242
+ foreach ($packages as $package=>$action) {
243
+ if ($action) {
244
+ $a = explode('|', $package);
245
+ $b = explode('|', $action);
246
+ $package = $a[1];
247
+ $channel = $a[0];
248
+ $version = '';
249
+ if ($b[0]=='upgrade') {
250
+ $version = $b[1];
251
+ }
252
+ $actions[$b[0]][] = array($channel, $package, $version, $version);
253
+ }
254
+ }
255
+ if (empty($actions)) {
256
+ $this->connect()->runHtmlConsole('No actions selected');
257
+ exit;
258
+ }
259
+
260
+ $this->controller()->startInstall();
261
+
262
+ $options = array();
263
+ if (!empty($ignoreLocalModification)) {
264
+ $options = array('ignorelocalmodification'=>1);
265
+ }
266
+ if(!$this->controller()->isWritable()||strlen($this->connect()->getConfig()->__get('remote_config'))>0){
267
+ $options['ftp'] = $this->connect()->getConfig()->__get('remote_config');
268
+ }
269
+
270
+ $this->controller()->channelConfig()->setCommandOptions($this->controller()->session(), $options);
271
+
272
+ foreach ($actions as $action=>$packages) {
273
+ foreach ($packages as $package) {
274
+ switch ($action) {
275
+ case 'install': case 'uninstall': case 'upgrade':
276
+ $this->connect()->runHtmlConsole(array(
277
+ 'command'=>$action,
278
+ 'options'=>$options,
279
+ 'params'=>$package
280
+ ));
281
+ break;
282
+
283
+ case 'reinstall':
284
+ $package_info = $this->connect()->getSingleConfig()->getPackage($package[0], $package[1]);
285
+ if (isset($package_info['version'])) {
286
+ $package[2] = $package_info['version'];
287
+ $package[3] = $package_info['version'];
288
+ }
289
+ $this->connect()->runHtmlConsole(array(
290
+ 'command'=>'install',
291
+ 'options'=>array_merge($options, array('force'=>1, 'nodeps'=>1)),
292
+ 'params'=>$package
293
+ ));
294
+ break;
295
+ }
296
+ }
297
+ }
298
+
299
+ $this->controller()->endInstall();
300
+ }
301
+
302
+
303
+ public function installUploadedPackage($file)
304
+ {
305
+ $this->controller()->startInstall();
306
+
307
+ $options = array();
308
+ if(!$this->controller()->isWritable()||strlen($this->connect()->getConfig()->__get('remote_config'))>0){
309
+ $options['ftp'] = $this->connect()->getConfig()->__get('remote_config');
310
+ }
311
+ $this->connect()->runHtmlConsole(array(
312
+ 'command'=>'install-file',
313
+ 'options'=>$options,
314
+ 'params'=>array($file),
315
+ ));
316
+ $this->controller()->endInstall();
317
+ }
318
+
319
+ /**
320
+ * Install package by id
321
+ *
322
+ * @param string $id
323
+ * @param boolean $force
324
+ */
325
+ public function installPackage($id, $force=false)
326
+ {
327
+ $match = array();
328
+ if (!$this->checkExtensionKey($id, $match)) {
329
+ $this->connect()->runHtmlConsole('Invalid package identifier provided: '.$id);
330
+ exit;
331
+ }
332
+
333
+ $channel = $match[1];
334
+ $package = $match[2];//.(!empty($match[3]) ? $match[3] : '');
335
+ $version = (!empty($match[3]) ? trim($match[3],'/\-') : '');
336
+
337
+ $this->controller()->startInstall();
338
+
339
+ $options = array();
340
+ if ($force) {
341
+ $options['force'] = 1;
342
+ }
343
+ if(!$this->controller()->isWritable()||strlen($this->connect()->getConfig()->__get('remote_config'))>0){
344
+ $options['ftp'] = $this->connect()->getConfig()->__get('remote_config');
345
+ }
346
+
347
+ $this->controller()->channelConfig()->setCommandOptions($this->controller()->session(), $options);
348
+
349
+ $this->connect()->runHtmlConsole(array(
350
+ 'command'=>'install',
351
+ 'options'=>$options,
352
+ 'params'=>array(0=>$channel, 1=>$package, 2=>$version),
353
+ ));
354
+
355
+ $this->controller()->endInstall();
356
+ }
357
+
358
+ /**
359
+ * Retrieve stability choosen client
360
+ *
361
+ * @return string alpha, beta, ...
362
+ */
363
+ public function getPreferredState()
364
+ {
365
+ if (is_null($this->get('preferred_state'))) {
366
+ $connectConfig = $this->connect()->getConfig();
367
+ $this->set('preferred_state', $connectConfig->__get('preferred_state'));
368
+ }
369
+ return $this->get('preferred_state');
370
+ }
371
+
372
+ /**
373
+ * Retrieve protocol choosen client
374
+ *
375
+ * @return string http, ftp
376
+ */
377
+ public function getProtocol()
378
+ {
379
+ if (is_null($this->get('protocol'))) {
380
+ $connectConfig = $this->connect()->getConfig();
381
+ $this->set('protocol', $connectConfig->__get('protocol'));
382
+ }
383
+ return $this->get('protocol');
384
+ }
385
+
386
+ /**
387
+ * Validate settings post data.
388
+ *
389
+ * @param array $p
390
+ */
391
+ public function validateConfigPost($p)
392
+ {
393
+ $errors = array();
394
+ $configTestFile = 'connect.cfgt';
395
+ $configObj = $this->connect()->getConfig();
396
+ if ('ftp' == $p['deployment_type'] || '1' == $p['inst_protocol']) {
397
+ /*check ftp*/
398
+
399
+ $confFile = $configObj->downloader_path.DIRECTORY_SEPARATOR.$configTestFile;
400
+ try {
401
+ $ftpObj = new Mage_Connect_Ftp();
402
+ $ftpObj->connect($p['ftp']);
403
+ $tempFile = tempnam(sys_get_temp_dir(),'config');
404
+ $serial = md5('config test file');
405
+ $f = @fopen($tempFile, "w+");
406
+ @fwrite($f, $serial);
407
+ @fclose($f);
408
+ $ret=$ftpObj->upload($confFile, $tempFile);
409
+
410
+ //read file
411
+ if (!$errors && is_file($configTestFile)) {
412
+ $size = filesize($configTestFile);
413
+ if(!$size) {
414
+ $errors[]='Unable to read saved settings. Please check Installation Path of FTP Connection.';
415
+ }
416
+
417
+ if (!$errors) {
418
+ $f = @fopen($configTestFile, "r");
419
+ @fseek($f, 0, SEEK_SET);
420
+
421
+ $contents = @fread($f, strlen($serial));
422
+ if ($serial != $contents) {
423
+ $errors[]='Wrong Installation Path of FTP Connection.';
424
+ }
425
+ fclose($f);
426
+ }
427
+ } else {
428
+ $errors[] = 'Unable to read saved settings. Please check Installation Path of FTP Connection.';
429
+ }
430
+ $ftpObj->delete($confFile);
431
+ $ftpObj->close();
432
+ } catch (Exception $e) {
433
+ $errors[] = 'Deployment FTP Error. ' . $e->getMessage();
434
+ }
435
+ } else {
436
+ $p['ftp'] = '';
437
+ }
438
+
439
+ if ('1' == $p['use_custom_permissions_mode']) {
440
+ /*check permissions*/
441
+ if (octdec(intval($p['mkdir_mode'])) < 73 || octdec(intval($p['mkdir_mode'])) > 511) {
442
+ $errors[]='Folders permissions not valid. ';
443
+ }
444
+ if (octdec(intval($p['chmod_file_mode'])) < 73 || octdec(intval($p['chmod_file_mode'])) > 511) {
445
+ $errors[]='Files permissions not valid. ';
446
+ }
447
+ }
448
+ //$this->controller()->session()->addMessage('success', 'Settings has been successfully saved');
449
+ return $errors;
450
+ }
451
+ /**
452
+ * Save settings.
453
+ *
454
+ * @param array $p
455
+ */
456
+ public function saveConfigPost($p)
457
+ {
458
+ $configObj = $this->connect()->getConfig();
459
+ if ('ftp' == $p['deployment_type'] || '1' == $p['inst_protocol']){
460
+ $this->set('ftp',$p['ftp']);
461
+ } else {
462
+ $p['ftp'] = '';
463
+ }
464
+ $configObj->remote_config = $p['ftp'];
465
+ $configObj->preferred_state = $p['preferred_state'];
466
+ $configObj->protocol = $p['protocol'];
467
+ $configObj->use_custom_permissions_mode = $p['use_custom_permissions_mode'];
468
+ if ('1' == $p['use_custom_permissions_mode']) {
469
+ $configObj->global_dir_mode = octdec(intval($p['mkdir_mode']));
470
+ $configObj->global_file_mode = octdec(intval($p['chmod_file_mode']));
471
+ }
472
+ if ($configObj->save()) {
473
+ $this->controller()->session()->addMessage('success', 'Settings has been successfully saved');
474
+ } else {
475
+ $this->controller()->session()->addMessage('error', 'Settings cannot be saved');
476
+ }
477
+ return $this;
478
+ }
479
+
480
+ /**
481
+ * Check Extension Key
482
+ *
483
+ * @param string $id
484
+ * @param array $match
485
+ * @return int
486
+ */
487
+ public function checkExtensionKey($id, &$match)
488
+ {
489
+ return preg_match('#^([^ ]+)\/([^-]+)(-.+)?$#', $id, $match);
490
+ }
491
+ }
downloader/Maged/Model/Connect/Request.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class request
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Model_Connect_Request extends Maged_Model
36
+ {
37
+ protected function _construct()
38
+ {
39
+ parent::_construct();
40
+ $this->set('success_callback', 'clear_cache({success:parent.onSuccess, fail:parent.onFailure})');
41
+ $this->set('failure_callback', 'parent.onFailure()');
42
+ }
43
+ }
downloader/Maged/Model/Dowloader.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Maged_Model_Downloader extends Maged_Model
28
+ {
29
+
30
+ }
downloader/Maged/Model/Session.php ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class session
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_Model_Session extends Maged_Model
36
+ {
37
+ /**
38
+ * Session
39
+ *
40
+ * @var Mage_Admin_Model_Session
41
+ */
42
+ protected $_session;
43
+
44
+ /**
45
+ * Init session
46
+ *
47
+ * @return Maged_Model_Session
48
+ */
49
+ public function start()
50
+ {
51
+ if (class_exists('Mage') && Mage::isInstalled()) {
52
+ // initialize Magento Config
53
+ Mage::app();
54
+ $this->_session = Mage::getSingleton('admin/session');
55
+ } else {
56
+ session_start();
57
+ }
58
+ return $this;
59
+ }
60
+
61
+ /**
62
+ * Get value by key
63
+ *
64
+ * @param string $key
65
+ * @return mixed
66
+ */
67
+ public function get($key)
68
+ {
69
+ return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
70
+ }
71
+
72
+ /**
73
+ * Set value for key
74
+ *
75
+ * @param string $key
76
+ * @param mixed $value
77
+ */
78
+ public function set($key, $value)
79
+ {
80
+ $_SESSION[$key] = $value;
81
+ return $this;
82
+ }
83
+
84
+ /**
85
+ * Authentication to downloader
86
+ */
87
+ public function authenticate()
88
+ {
89
+ if (!$this->_session) {
90
+ return $this;
91
+ }
92
+
93
+ if (!empty($_GET['return'])) {
94
+ $this->set('return_url', $_GET['return']);
95
+ }
96
+
97
+ if ($this->_checkUserAccess()) {
98
+ return $this;
99
+ }
100
+
101
+ if (!$this->controller()->isInstalled()) {
102
+ return $this;
103
+ }
104
+
105
+ try {
106
+ if ( (isset($_POST['username']) && empty($_POST['username']))
107
+ || (isset($_POST['password']) && empty($_POST['password']))) {
108
+ $this->addMessage('error', 'Invalid user name or password');
109
+ }
110
+ if (empty($_POST['username']) || empty($_POST['password'])) {
111
+ $this->controller()->setAction('login');
112
+ return $this;
113
+ }
114
+ $user = $this->_session->login($_POST['username'], $_POST['password']);
115
+ $this->_session->refreshAcl();
116
+ if ($this->_checkUserAccess($user)) {
117
+ return $this;
118
+ }
119
+ } catch (Exception $e) {
120
+ $this->addMessage('error', $e->getMessage());
121
+ }
122
+
123
+ $this->controller()
124
+ ->redirect(
125
+ $this->controller()->url('loggedin'),
126
+ true
127
+ );
128
+ }
129
+
130
+ /**
131
+ * Check is user logged in and permissions
132
+ *
133
+ * @param Mage_Admin_Model_User|null $user
134
+ * @return bool
135
+ */
136
+ protected function _checkUserAccess($user = null)
137
+ {
138
+ if ($user && !$user->getId()) {
139
+ $this->addMessage('error', 'Invalid user name or password');
140
+ $this->controller()->setAction('login');
141
+ } elseif ($this->getUserId() || ($user && $user->getId())) {
142
+ if ($this->_session->isAllowed('all')) {
143
+ return true;
144
+ } else {
145
+ $this->logout();
146
+ $this->addMessage('error', 'Access Denied', true);
147
+ $this->controller()->setAction('login');
148
+ }
149
+ }
150
+ return false;
151
+ }
152
+
153
+ /**
154
+ * Log Out
155
+ *
156
+ * @return Maged_Model_Session
157
+ */
158
+ public function logout()
159
+ {
160
+ if (!$this->_session) {
161
+ return $this;
162
+ }
163
+ $this->_session->unsUser();
164
+ return $this;
165
+ }
166
+
167
+ /**
168
+ * Retrieve user
169
+ *
170
+ * @return mixed
171
+ */
172
+ public function getUserId()
173
+ {
174
+ if (($session = $this->_session) && ($user = $session->getUser())) {
175
+ return $user->getId();
176
+ }
177
+ return false;
178
+ }
179
+
180
+ /**
181
+ * Add Message
182
+ *
183
+ * @param string $type
184
+ * @param string $msg
185
+ * @param string $clear
186
+ * @return Maged_Model_Session
187
+ */
188
+ public function addMessage($type, $msg, $clear = false)
189
+ {
190
+ $msgs = $this->getMessages($clear);
191
+ $msgs[$type][] = $msg;
192
+ $this->set('messages', $msgs);
193
+ return $this;
194
+ }
195
+
196
+ /**
197
+ * Retrieve messages from cache
198
+ *
199
+ * @param boolean $clear
200
+ * @return mixed
201
+ */
202
+ public function getMessages($clear = true)
203
+ {
204
+ $msgs = $this->get('messages');
205
+ $msgs = $msgs ? $msgs : array();
206
+ if ($clear) {
207
+ unset($_SESSION['messages']);
208
+ }
209
+ return $msgs;
210
+ }
211
+
212
+ /**
213
+ * Retrieve url to adminhtml
214
+ *
215
+ * @return string
216
+ */
217
+ public function getReturnUrl()
218
+ {
219
+ if (!$this->_session || !$this->_session->isLoggedIn()) {
220
+ return '';
221
+ }
222
+ return Mage::getSingleton('adminhtml/url')->getUrl('adminhtml');
223
+ }
224
+ }
downloader/Maged/View.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class for viewer
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
33
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
+ */
35
+ class Maged_View
36
+ {
37
+ /**
38
+ * Internal cache
39
+ *
40
+ * @var array
41
+ */
42
+ protected $_data = array();
43
+
44
+ /**
45
+ * Constructor
46
+ */
47
+ public function __construct()
48
+ {
49
+
50
+ }
51
+
52
+ /**
53
+ * Retrieve Controller as singleton
54
+ *
55
+ * @return Maged_Controller
56
+ */
57
+ public function controller()
58
+ {
59
+ return Maged_Controller::singleton();
60
+ }
61
+
62
+ /**
63
+ * Create url by action and params
64
+ *
65
+ * @param mixed $action
66
+ * @param mixed $params
67
+ * @return string
68
+ */
69
+ public function url($action='', $params=array())
70
+ {
71
+ return $this->controller()->url($action, $params);
72
+ }
73
+
74
+ /**
75
+ * Retrieve base url
76
+ *
77
+ * @return string
78
+ */
79
+ public function baseUrl()
80
+ {
81
+ return str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));
82
+ }
83
+
84
+ /**
85
+ * Retrieve url of magento
86
+ *
87
+ * @return string
88
+ */
89
+ public function mageUrl()
90
+ {
91
+ return str_replace('\\', '/', dirname($this->baseUrl()));
92
+ }
93
+
94
+ /**
95
+ * Include template
96
+ *
97
+ * @param string $name
98
+ * @return string
99
+ */
100
+ public function template($name)
101
+ {
102
+ ob_start();
103
+ include $this->controller()->filepath('template/'.$name);
104
+ return ob_get_clean();
105
+ }
106
+
107
+ /**
108
+ * Set value for key
109
+ *
110
+ * @param string $key
111
+ * @param mixed $value
112
+ * @return Maged_Controller
113
+ */
114
+ public function set($key, $value)
115
+ {
116
+ $this->_data[$key] = $value;
117
+ return $this;
118
+ }
119
+
120
+ /**
121
+ * Get value by key
122
+ *
123
+ * @param string $key
124
+ * @return mixed
125
+ */
126
+ public function get($key)
127
+ {
128
+ return isset($this->_data[$key]) ? $this->_data[$key] : null;
129
+ }
130
+
131
+ /**
132
+ * Translator
133
+ *
134
+ * @param string $string
135
+ * @return string
136
+ */
137
+ public function __($string)
138
+ {
139
+ return $string;
140
+ }
141
+
142
+ /**
143
+ * Retrieve link for header menu
144
+ *
145
+ * @param mixed $action
146
+ */
147
+ public function getNavLinkParams($action)
148
+ {
149
+ $params = 'href="'.$this->url($action).'"';
150
+ if ($this->controller()->getAction()==$action) {
151
+ $params .= ' class="active"';
152
+ }
153
+ return $params;
154
+ }
155
+ }
downloader/config.ini ADDED
@@ -0,0 +1 @@
 
1
+ root_channel=community
downloader/index.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ if (version_compare(phpversion(), '5.2.0', '<')===true) {
28
+ echo '<div style="font:12px/1.35em arial, helvetica, sans-serif;"><div style="margin:0 0 25px 0; border-bottom:1px solid #ccc;"><h3 style="margin:0; font-size:1.7em; font-weight:normal; text-transform:none; text-align:left; color:#2f2f2f;">Whoops, it looks like you have an invalid PHP version.</h3></div><p>Magento supports PHP 5.2.0 or newer. <a href="http://www.magentocommerce.com/install" target="">Find out</a> how to install</a> Magento using PHP-CGI as a work-around.</p></div>';
29
+ exit;
30
+ }
31
+
32
+ require_once("lib/Mage/Autoload/Simple.php");
33
+ Mage_Autoload_Simple::register();
34
+
35
+ umask(0);
36
+ Maged_Controller::run();
downloader/js/prototype.js ADDED
@@ -0,0 +1,3277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Prototype JavaScript framework, version 1.5.1.1
2
+ * (c) 2005-2007 Sam Stephenson
3
+ *
4
+ * Prototype is freely distributable under the terms of an MIT-style license.
5
+ * For details, see the Prototype web site: http://www.prototypejs.org/
6
+ *
7
+ /*--------------------------------------------------------------------------*/
8
+
9
+ var Prototype = {
10
+ Version: '1.5.1.1',
11
+
12
+ Browser: {
13
+ IE: !!(window.attachEvent && !window.opera),
14
+ Opera: !!window.opera,
15
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
17
+ },
18
+
19
+ BrowserFeatures: {
20
+ XPath: !!document.evaluate,
21
+ ElementExtensions: !!window.HTMLElement,
22
+ SpecificElementExtensions:
23
+ (document.createElement('div').__proto__ !==
24
+ document.createElement('form').__proto__)
25
+ },
26
+
27
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
28
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
29
+
30
+ emptyFunction: function() { },
31
+ K: function(x) { return x }
32
+ }
33
+
34
+ var Class = {
35
+ create: function() {
36
+ return function() {
37
+ this.initialize.apply(this, arguments);
38
+ }
39
+ }
40
+ }
41
+
42
+ var Abstract = new Object();
43
+
44
+ Object.extend = function(destination, source) {
45
+ for (var property in source) {
46
+ destination[property] = source[property];
47
+ }
48
+ return destination;
49
+ }
50
+
51
+ Object.extend(Object, {
52
+ inspect: function(object) {
53
+ try {
54
+ if (object === undefined) return 'undefined';
55
+ if (object === null) return 'null';
56
+ return object.inspect ? object.inspect() : object.toString();
57
+ } catch (e) {
58
+ if (e instanceof RangeError) return '...';
59
+ throw e;
60
+ }
61
+ },
62
+
63
+ toJSON: function(object) {
64
+ var type = typeof object;
65
+ switch(type) {
66
+ case 'undefined':
67
+ case 'function':
68
+ case 'unknown': return;
69
+ case 'boolean': return object.toString();
70
+ }
71
+ if (object === null) return 'null';
72
+ if (object.toJSON) return object.toJSON();
73
+ if (object.ownerDocument === document) return;
74
+ var results = [];
75
+ for (var property in object) {
76
+ var value = Object.toJSON(object[property]);
77
+ if (value !== undefined)
78
+ results.push(property.toJSON() + ': ' + value);
79
+ }
80
+ return '{' + results.join(', ') + '}';
81
+ },
82
+
83
+ keys: function(object) {
84
+ var keys = [];
85
+ for (var property in object)
86
+ keys.push(property);
87
+ return keys;
88
+ },
89
+
90
+ values: function(object) {
91
+ var values = [];
92
+ for (var property in object)
93
+ values.push(object[property]);
94
+ return values;
95
+ },
96
+
97
+ clone: function(object) {
98
+ return Object.extend({}, object);
99
+ }
100
+ });
101
+
102
+ Function.prototype.bind = function() {
103
+ var __method = this, args = $A(arguments), object = args.shift();
104
+ return function() {
105
+ return __method.apply(object, args.concat($A(arguments)));
106
+ }
107
+ }
108
+
109
+ Function.prototype.bindAsEventListener = function(object) {
110
+ var __method = this, args = $A(arguments), object = args.shift();
111
+ return function(event) {
112
+ return __method.apply(object, [event || window.event].concat(args));
113
+ }
114
+ }
115
+
116
+ Object.extend(Number.prototype, {
117
+ toColorPart: function() {
118
+ return this.toPaddedString(2, 16);
119
+ },
120
+
121
+ succ: function() {
122
+ return this + 1;
123
+ },
124
+
125
+ times: function(iterator) {
126
+ $R(0, this, true).each(iterator);
127
+ return this;
128
+ },
129
+
130
+ toPaddedString: function(length, radix) {
131
+ var string = this.toString(radix || 10);
132
+ return '0'.times(length - string.length) + string;
133
+ },
134
+
135
+ toJSON: function() {
136
+ return isFinite(this) ? this.toString() : 'null';
137
+ }
138
+ });
139
+
140
+ Date.prototype.toJSON = function() {
141
+ return '"' + this.getFullYear() + '-' +
142
+ (this.getMonth() + 1).toPaddedString(2) + '-' +
143
+ this.getDate().toPaddedString(2) + 'T' +
144
+ this.getHours().toPaddedString(2) + ':' +
145
+ this.getMinutes().toPaddedString(2) + ':' +
146
+ this.getSeconds().toPaddedString(2) + '"';
147
+ };
148
+
149
+ var Try = {
150
+ these: function() {
151
+ var returnValue;
152
+
153
+ for (var i = 0, length = arguments.length; i < length; i++) {
154
+ var lambda = arguments[i];
155
+ try {
156
+ returnValue = lambda();
157
+ break;
158
+ } catch (e) {}
159
+ }
160
+
161
+ return returnValue;
162
+ }
163
+ }
164
+
165
+ /*--------------------------------------------------------------------------*/
166
+
167
+ var PeriodicalExecuter = Class.create();
168
+ PeriodicalExecuter.prototype = {
169
+ initialize: function(callback, frequency) {
170
+ this.callback = callback;
171
+ this.frequency = frequency;
172
+ this.currentlyExecuting = false;
173
+
174
+ this.registerCallback();
175
+ },
176
+
177
+ registerCallback: function() {
178
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
179
+ },
180
+
181
+ stop: function() {
182
+ if (!this.timer) return;
183
+ clearInterval(this.timer);
184
+ this.timer = null;
185
+ },
186
+
187
+ onTimerEvent: function() {
188
+ if (!this.currentlyExecuting) {
189
+ try {
190
+ this.currentlyExecuting = true;
191
+ this.callback(this);
192
+ } finally {
193
+ this.currentlyExecuting = false;
194
+ }
195
+ }
196
+ }
197
+ }
198
+ Object.extend(String, {
199
+ interpret: function(value) {
200
+ return value == null ? '' : String(value);
201
+ },
202
+ specialChar: {
203
+ '\b': '\\b',
204
+ '\t': '\\t',
205
+ '\n': '\\n',
206
+ '\f': '\\f',
207
+ '\r': '\\r',
208
+ '\\': '\\\\'
209
+ }
210
+ });
211
+
212
+ Object.extend(String.prototype, {
213
+ gsub: function(pattern, replacement) {
214
+ var result = '', source = this, match;
215
+ replacement = arguments.callee.prepareReplacement(replacement);
216
+
217
+ while (source.length > 0) {
218
+ if (match = source.match(pattern)) {
219
+ result += source.slice(0, match.index);
220
+ result += String.interpret(replacement(match));
221
+ source = source.slice(match.index + match[0].length);
222
+ } else {
223
+ result += source, source = '';
224
+ }
225
+ }
226
+ return result;
227
+ },
228
+
229
+ sub: function(pattern, replacement, count) {
230
+ replacement = this.gsub.prepareReplacement(replacement);
231
+ count = count === undefined ? 1 : count;
232
+
233
+ return this.gsub(pattern, function(match) {
234
+ if (--count < 0) return match[0];
235
+ return replacement(match);
236
+ });
237
+ },
238
+
239
+ scan: function(pattern, iterator) {
240
+ this.gsub(pattern, iterator);
241
+ return this;
242
+ },
243
+
244
+ truncate: function(length, truncation) {
245
+ length = length || 30;
246
+ truncation = truncation === undefined ? '...' : truncation;
247
+ return this.length > length ?
248
+ this.slice(0, length - truncation.length) + truncation : this;
249
+ },
250
+
251
+ strip: function() {
252
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
253
+ },
254
+
255
+ stripTags: function() {
256
+ return this.replace(/<\/?[^>]+>/gi, '');
257
+ },
258
+
259
+ stripScripts: function() {
260
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
261
+ },
262
+
263
+ extractScripts: function() {
264
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
265
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
266
+ return (this.match(matchAll) || []).map(function(scriptTag) {
267
+ return (scriptTag.match(matchOne) || ['', ''])[1];
268
+ });
269
+ },
270
+
271
+ evalScripts: function() {
272
+ return this.extractScripts().map(function(script) { return eval(script) });
273
+ },
274
+
275
+ escapeHTML: function() {
276
+ var self = arguments.callee;
277
+ self.text.data = this;
278
+ return self.div.innerHTML;
279
+ },
280
+
281
+ unescapeHTML: function() {
282
+ var div = document.createElement('div');
283
+ div.innerHTML = this.stripTags();
284
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
285
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
286
+ div.childNodes[0].nodeValue) : '';
287
+ },
288
+
289
+ toQueryParams: function(separator) {
290
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
291
+ if (!match) return {};
292
+
293
+ return match[1].split(separator || '&').inject({}, function(hash, pair) {
294
+ if ((pair = pair.split('='))[0]) {
295
+ var key = decodeURIComponent(pair.shift());
296
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
297
+ if (value != undefined) value = decodeURIComponent(value);
298
+
299
+ if (key in hash) {
300
+ if (hash[key].constructor != Array) hash[key] = [hash[key]];
301
+ hash[key].push(value);
302
+ }
303
+ else hash[key] = value;
304
+ }
305
+ return hash;
306
+ });
307
+ },
308
+
309
+ toArray: function() {
310
+ return this.split('');
311
+ },
312
+
313
+ succ: function() {
314
+ return this.slice(0, this.length - 1) +
315
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
316
+ },
317
+
318
+ times: function(count) {
319
+ var result = '';
320
+ for (var i = 0; i < count; i++) result += this;
321
+ return result;
322
+ },
323
+
324
+ camelize: function() {
325
+ var parts = this.split('-'), len = parts.length;
326
+ if (len == 1) return parts[0];
327
+
328
+ var camelized = this.charAt(0) == '-'
329
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
330
+ : parts[0];
331
+
332
+ for (var i = 1; i < len; i++)
333
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
334
+
335
+ return camelized;
336
+ },
337
+
338
+ capitalize: function() {
339
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
340
+ },
341
+
342
+ underscore: function() {
343
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
344
+ },
345
+
346
+ dasherize: function() {
347
+ return this.gsub(/_/,'-');
348
+ },
349
+
350
+ inspect: function(useDoubleQuotes) {
351
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
352
+ var character = String.specialChar[match[0]];
353
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
354
+ });
355
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
356
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
357
+ },
358
+
359
+ toJSON: function() {
360
+ return this.inspect(true);
361
+ },
362
+
363
+ unfilterJSON: function(filter) {
364
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
365
+ },
366
+
367
+ isJSON: function() {
368
+ var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
369
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
370
+ },
371
+
372
+ evalJSON: function(sanitize) {
373
+ var json = this.unfilterJSON();
374
+ try {
375
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
376
+ } catch (e) { }
377
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
378
+ },
379
+
380
+ include: function(pattern) {
381
+ return this.indexOf(pattern) > -1;
382
+ },
383
+
384
+ startsWith: function(pattern) {
385
+ return this.indexOf(pattern) === 0;
386
+ },
387
+
388
+ endsWith: function(pattern) {
389
+ var d = this.length - pattern.length;
390
+ return d >= 0 && this.lastIndexOf(pattern) === d;
391
+ },
392
+
393
+ empty: function() {
394
+ return this == '';
395
+ },
396
+
397
+ blank: function() {
398
+ return /^\s*$/.test(this);
399
+ }
400
+ });
401
+
402
+ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
403
+ escapeHTML: function() {
404
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
405
+ },
406
+ unescapeHTML: function() {
407
+ return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
408
+ }
409
+ });
410
+
411
+ String.prototype.gsub.prepareReplacement = function(replacement) {
412
+ if (typeof replacement == 'function') return replacement;
413
+ var template = new Template(replacement);
414
+ return function(match) { return template.evaluate(match) };
415
+ }
416
+
417
+ String.prototype.parseQuery = String.prototype.toQueryParams;
418
+
419
+ Object.extend(String.prototype.escapeHTML, {
420
+ div: document.createElement('div'),
421
+ text: document.createTextNode('')
422
+ });
423
+
424
+ with (String.prototype.escapeHTML) div.appendChild(text);
425
+
426
+ var Template = Class.create();
427
+ Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
428
+ Template.prototype = {
429
+ initialize: function(template, pattern) {
430
+ this.template = template.toString();
431
+ this.pattern = pattern || Template.Pattern;
432
+ },
433
+
434
+ evaluate: function(object) {
435
+ return this.template.gsub(this.pattern, function(match) {
436
+ var before = match[1];
437
+ if (before == '\\') return match[2];
438
+ return before + String.interpret(object[match[3]]);
439
+ });
440
+ }
441
+ }
442
+
443
+ var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
444
+
445
+ var Enumerable = {
446
+ each: function(iterator) {
447
+ var index = 0;
448
+ try {
449
+ this._each(function(value) {
450
+ iterator(value, index++);
451
+ });
452
+ } catch (e) {
453
+ if (e != $break) throw e;
454
+ }
455
+ return this;
456
+ },
457
+
458
+ eachSlice: function(number, iterator) {
459
+ var index = -number, slices = [], array = this.toArray();
460
+ while ((index += number) < array.length)
461
+ slices.push(array.slice(index, index+number));
462
+ return slices.map(iterator);
463
+ },
464
+
465
+ all: function(iterator) {
466
+ var result = true;
467
+ this.each(function(value, index) {
468
+ result = result && !!(iterator || Prototype.K)(value, index);
469
+ if (!result) throw $break;
470
+ });
471
+ return result;
472
+ },
473
+
474
+ any: function(iterator) {
475
+ var result = false;
476
+ this.each(function(value, index) {
477
+ if (result = !!(iterator || Prototype.K)(value, index))
478
+ throw $break;
479
+ });
480
+ return result;
481
+ },
482
+
483
+ collect: function(iterator) {
484
+ var results = [];
485
+ this.each(function(value, index) {
486
+ results.push((iterator || Prototype.K)(value, index));
487
+ });
488
+ return results;
489
+ },
490
+
491
+ detect: function(iterator) {
492
+ var result;
493
+ this.each(function(value, index) {
494
+ if (iterator(value, index)) {
495
+ result = value;
496
+ throw $break;
497
+ }
498
+ });
499
+ return result;
500
+ },
501
+
502
+ findAll: function(iterator) {
503
+ var results = [];
504
+ this.each(function(value, index) {
505
+ if (iterator(value, index))
506
+ results.push(value);
507
+ });
508
+ return results;
509
+ },
510
+
511
+ grep: function(pattern, iterator) {
512
+ var results = [];
513
+ this.each(function(value, index) {
514
+ var stringValue = value.toString();
515
+ if (stringValue.match(pattern))
516
+ results.push((iterator || Prototype.K)(value, index));
517
+ })
518
+ return results;
519
+ },
520
+
521
+ include: function(object) {
522
+ var found = false;
523
+ this.each(function(value) {
524
+ if (value == object) {
525
+ found = true;
526
+ throw $break;
527
+ }
528
+ });
529
+ return found;
530
+ },
531
+
532
+ inGroupsOf: function(number, fillWith) {
533
+ fillWith = fillWith === undefined ? null : fillWith;
534
+ return this.eachSlice(number, function(slice) {
535
+ while(slice.length < number) slice.push(fillWith);
536
+ return slice;
537
+ });
538
+ },
539
+
540
+ inject: function(memo, iterator) {
541
+ this.each(function(value, index) {
542
+ memo = iterator(memo, value, index);
543
+ });
544
+ return memo;
545
+ },
546
+
547
+ invoke: function(method) {
548
+ var args = $A(arguments).slice(1);
549
+ return this.map(function(value) {
550
+ return value[method].apply(value, args);
551
+ });
552
+ },
553
+
554
+ max: function(iterator) {
555
+ var result;
556
+ this.each(function(value, index) {
557
+ value = (iterator || Prototype.K)(value, index);
558
+ if (result == undefined || value >= result)
559
+ result = value;
560
+ });
561
+ return result;
562
+ },
563
+
564
+ min: function(iterator) {
565
+ var result;
566
+ this.each(function(value, index) {
567
+ value = (iterator || Prototype.K)(value, index);
568
+ if (result == undefined || value < result)
569
+ result = value;
570
+ });
571
+ return result;
572
+ },
573
+
574
+ partition: function(iterator) {
575
+ var trues = [], falses = [];
576
+ this.each(function(value, index) {
577
+ ((iterator || Prototype.K)(value, index) ?
578
+ trues : falses).push(value);
579
+ });
580
+ return [trues, falses];
581
+ },
582
+
583
+ pluck: function(property) {
584
+ var results = [];
585
+ this.each(function(value, index) {
586
+ results.push(value[property]);
587
+ });
588
+ return results;
589
+ },
590
+
591
+ reject: function(iterator) {
592
+ var results = [];
593
+ this.each(function(value, index) {
594
+ if (!iterator(value, index))
595
+ results.push(value);
596
+ });
597
+ return results;
598
+ },
599
+
600
+ sortBy: function(iterator) {
601
+ return this.map(function(value, index) {
602
+ return {value: value, criteria: iterator(value, index)};
603
+ }).sort(function(left, right) {
604
+ var a = left.criteria, b = right.criteria;
605
+ return a < b ? -1 : a > b ? 1 : 0;
606
+ }).pluck('value');
607
+ },
608
+
609
+ toArray: function() {
610
+ return this.map();
611
+ },
612
+
613
+ zip: function() {
614
+ var iterator = Prototype.K, args = $A(arguments);
615
+ if (typeof args.last() == 'function')
616
+ iterator = args.pop();
617
+
618
+ var collections = [this].concat(args).map($A);
619
+ return this.map(function(value, index) {
620
+ return iterator(collections.pluck(index));
621
+ });
622
+ },
623
+
624
+ size: function() {
625
+ return this.toArray().length;
626
+ },
627
+
628
+ inspect: function() {
629
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
630
+ }
631
+ }
632
+
633
+ Object.extend(Enumerable, {
634
+ map: Enumerable.collect,
635
+ find: Enumerable.detect,
636
+ select: Enumerable.findAll,
637
+ member: Enumerable.include,
638
+ entries: Enumerable.toArray
639
+ });
640
+ var $A = Array.from = function(iterable) {
641
+ if (!iterable) return [];
642
+ if (iterable.toArray) {
643
+ return iterable.toArray();
644
+ } else {
645
+ var results = [];
646
+ for (var i = 0, length = iterable.length; i < length; i++)
647
+ results.push(iterable[i]);
648
+ return results;
649
+ }
650
+ }
651
+
652
+ if (Prototype.Browser.WebKit) {
653
+ $A = Array.from = function(iterable) {
654
+ if (!iterable) return [];
655
+ if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
656
+ iterable.toArray) {
657
+ return iterable.toArray();
658
+ } else {
659
+ var results = [];
660
+ for (var i = 0, length = iterable.length; i < length; i++)
661
+ results.push(iterable[i]);
662
+ return results;
663
+ }
664
+ }
665
+ }
666
+
667
+ Object.extend(Array.prototype, Enumerable);
668
+
669
+ if (!Array.prototype._reverse)
670
+ Array.prototype._reverse = Array.prototype.reverse;
671
+
672
+ Object.extend(Array.prototype, {
673
+ _each: function(iterator) {
674
+ for (var i = 0, length = this.length; i < length; i++)
675
+ iterator(this[i]);
676
+ },
677
+
678
+ clear: function() {
679
+ this.length = 0;
680
+ return this;
681
+ },
682
+
683
+ first: function() {
684
+ return this[0];
685
+ },
686
+
687
+ last: function() {
688
+ return this[this.length - 1];
689
+ },
690
+
691
+ compact: function() {
692
+ return this.select(function(value) {
693
+ return value != null;
694
+ });
695
+ },
696
+
697
+ flatten: function() {
698
+ return this.inject([], function(array, value) {
699
+ return array.concat(value && value.constructor == Array ?
700
+ value.flatten() : [value]);
701
+ });
702
+ },
703
+
704
+ without: function() {
705
+ var values = $A(arguments);
706
+ return this.select(function(value) {
707
+ return !values.include(value);
708
+ });
709
+ },
710
+
711
+ indexOf: function(object) {
712
+ for (var i = 0, length = this.length; i < length; i++)
713
+ if (this[i] == object) return i;
714
+ return -1;
715
+ },
716
+
717
+ reverse: function(inline) {
718
+ return (inline !== false ? this : this.toArray())._reverse();
719
+ },
720
+
721
+ reduce: function() {
722
+ return this.length > 1 ? this : this[0];
723
+ },
724
+
725
+ uniq: function(sorted) {
726
+ return this.inject([], function(array, value, index) {
727
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
728
+ array.push(value);
729
+ return array;
730
+ });
731
+ },
732
+
733
+ clone: function() {
734
+ return [].concat(this);
735
+ },
736
+
737
+ size: function() {
738
+ return this.length;
739
+ },
740
+
741
+ inspect: function() {
742
+ return '[' + this.map(Object.inspect).join(', ') + ']';
743
+ },
744
+
745
+ toJSON: function() {
746
+ var results = [];
747
+ this.each(function(object) {
748
+ var value = Object.toJSON(object);
749
+ if (value !== undefined) results.push(value);
750
+ });
751
+ return '[' + results.join(', ') + ']';
752
+ }
753
+ });
754
+
755
+ Array.prototype.toArray = Array.prototype.clone;
756
+
757
+ function $w(string) {
758
+ string = string.strip();
759
+ return string ? string.split(/\s+/) : [];
760
+ }
761
+
762
+ if (Prototype.Browser.Opera){
763
+ Array.prototype.concat = function() {
764
+ var array = [];
765
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
766
+ for (var i = 0, length = arguments.length; i < length; i++) {
767
+ if (arguments[i].constructor == Array) {
768
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
769
+ array.push(arguments[i][j]);
770
+ } else {
771
+ array.push(arguments[i]);
772
+ }
773
+ }
774
+ return array;
775
+ }
776
+ }
777
+ var Hash = function(object) {
778
+ if (object instanceof Hash) this.merge(object);
779
+ else Object.extend(this, object || {});
780
+ };
781
+
782
+ Object.extend(Hash, {
783
+ toQueryString: function(obj) {
784
+ var parts = [];
785
+ parts.add = arguments.callee.addPair;
786
+
787
+ this.prototype._each.call(obj, function(pair) {
788
+ if (!pair.key) return;
789
+ var value = pair.value;
790
+
791
+ if (value && typeof value == 'object') {
792
+ if (value.constructor == Array) value.each(function(value) {
793
+ parts.add(pair.key, value);
794
+ });
795
+ return;
796
+ }
797
+ parts.add(pair.key, value);
798
+ });
799
+
800
+ return parts.join('&');
801
+ },
802
+
803
+ toJSON: function(object) {
804
+ var results = [];
805
+ this.prototype._each.call(object, function(pair) {
806
+ var value = Object.toJSON(pair.value);
807
+ if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
808
+ });
809
+ return '{' + results.join(', ') + '}';
810
+ }
811
+ });
812
+
813
+ Hash.toQueryString.addPair = function(key, value, prefix) {
814
+ key = encodeURIComponent(key);
815
+ if (value === undefined) this.push(key);
816
+ else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
817
+ }
818
+
819
+ Object.extend(Hash.prototype, Enumerable);
820
+ Object.extend(Hash.prototype, {
821
+ _each: function(iterator) {
822
+ for (var key in this) {
823
+ var value = this[key];
824
+ if (value && value == Hash.prototype[key]) continue;
825
+
826
+ var pair = [key, value];
827
+ pair.key = key;
828
+ pair.value = value;
829
+ iterator(pair);
830
+ }
831
+ },
832
+
833
+ keys: function() {
834
+ return this.pluck('key');
835
+ },
836
+
837
+ values: function() {
838
+ return this.pluck('value');
839
+ },
840
+
841
+ merge: function(hash) {
842
+ return $H(hash).inject(this, function(mergedHash, pair) {
843
+ mergedHash[pair.key] = pair.value;
844
+ return mergedHash;
845
+ });
846
+ },
847
+
848
+ remove: function() {
849
+ var result;
850
+ for(var i = 0, length = arguments.length; i < length; i++) {
851
+ var value = this[arguments[i]];
852
+ if (value !== undefined){
853
+ if (result === undefined) result = value;
854
+ else {
855
+ if (result.constructor != Array) result = [result];
856
+ result.push(value)
857
+ }
858
+ }
859
+ delete this[arguments[i]];
860
+ }
861
+ return result;
862
+ },
863
+
864
+ toQueryString: function() {
865
+ return Hash.toQueryString(this);
866
+ },
867
+
868
+ inspect: function() {
869
+ return '#<Hash:{' + this.map(function(pair) {
870
+ return pair.map(Object.inspect).join(': ');
871
+ }).join(', ') + '}>';
872
+ },
873
+
874
+ toJSON: function() {
875
+ return Hash.toJSON(this);
876
+ }
877
+ });
878
+
879
+ function $H(object) {
880
+ if (object instanceof Hash) return object;
881
+ return new Hash(object);
882
+ };
883
+
884
+ // Safari iterates over shadowed properties
885
+ if (function() {
886
+ var i = 0, Test = function(value) { this.key = value };
887
+ Test.prototype.key = 'foo';
888
+ for (var property in new Test('bar')) i++;
889
+ return i > 1;
890
+ }()) Hash.prototype._each = function(iterator) {
891
+ var cache = [];
892
+ for (var key in this) {
893
+ var value = this[key];
894
+ if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
895
+ cache.push(key);
896
+ var pair = [key, value];
897
+ pair.key = key;
898
+ pair.value = value;
899
+ iterator(pair);
900
+ }
901
+ };
902
+ ObjectRange = Class.create();
903
+ Object.extend(ObjectRange.prototype, Enumerable);
904
+ Object.extend(ObjectRange.prototype, {
905
+ initialize: function(start, end, exclusive) {
906
+ this.start = start;
907
+ this.end = end;
908
+ this.exclusive = exclusive;
909
+ },
910
+
911
+ _each: function(iterator) {
912
+ var value = this.start;
913
+ while (this.include(value)) {
914
+ iterator(value);
915
+ value = value.succ();
916
+ }
917
+ },
918
+
919
+ include: function(value) {
920
+ if (value < this.start)
921
+ return false;
922
+ if (this.exclusive)
923
+ return value < this.end;
924
+ return value <= this.end;
925
+ }
926
+ });
927
+
928
+ var $R = function(start, end, exclusive) {
929
+ return new ObjectRange(start, end, exclusive);
930
+ }
931
+
932
+ var Ajax = {
933
+ getTransport: function() {
934
+ return Try.these(
935
+ function() {return new XMLHttpRequest()},
936
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
937
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
938
+ ) || false;
939
+ },
940
+
941
+ activeRequestCount: 0
942
+ }
943
+
944
+ Ajax.Responders = {
945
+ responders: [],
946
+
947
+ _each: function(iterator) {
948
+ this.responders._each(iterator);
949
+ },
950
+
951
+ register: function(responder) {
952
+ if (!this.include(responder))
953
+ this.responders.push(responder);
954
+ },
955
+
956
+ unregister: function(responder) {
957
+ this.responders = this.responders.without(responder);
958
+ },
959
+
960
+ dispatch: function(callback, request, transport, json) {
961
+ this.each(function(responder) {
962
+ if (typeof responder[callback] == 'function') {
963
+ try {
964
+ responder[callback].apply(responder, [request, transport, json]);
965
+ } catch (e) {}
966
+ }
967
+ });
968
+ }
969
+ };
970
+
971
+ Object.extend(Ajax.Responders, Enumerable);
972
+
973
+ Ajax.Responders.register({
974
+ onCreate: function() {
975
+ Ajax.activeRequestCount++;
976
+ },
977
+ onComplete: function() {
978
+ Ajax.activeRequestCount--;
979
+ }
980
+ });
981
+
982
+ Ajax.Base = function() {};
983
+ Ajax.Base.prototype = {
984
+ setOptions: function(options) {
985
+ this.options = {
986
+ method: 'post',
987
+ asynchronous: true,
988
+ contentType: 'application/x-www-form-urlencoded',
989
+ encoding: 'UTF-8',
990
+ parameters: ''
991
+ }
992
+ Object.extend(this.options, options || {});
993
+
994
+ this.options.method = this.options.method.toLowerCase();
995
+ if (typeof this.options.parameters == 'string')
996
+ this.options.parameters = this.options.parameters.toQueryParams();
997
+ }
998
+ }
999
+
1000
+ Ajax.Request = Class.create();
1001
+ Ajax.Request.Events =
1002
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1003
+
1004
+ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
1005
+ _complete: false,
1006
+
1007
+ initialize: function(url, options) {
1008
+ this.transport = Ajax.getTransport();
1009
+ this.setOptions(options);
1010
+ this.request(url);
1011
+ },
1012
+
1013
+ request: function(url) {
1014
+ this.url = url;
1015
+ this.method = this.options.method;
1016
+ var params = Object.clone(this.options.parameters);
1017
+
1018
+ if (!['get', 'post'].include(this.method)) {
1019
+ // simulate other verbs over post
1020
+ params['_method'] = this.method;
1021
+ this.method = 'post';
1022
+ }
1023
+
1024
+ this.parameters = params;
1025
+
1026
+ if (params = Hash.toQueryString(params)) {
1027
+ // when GET, append parameters to URL
1028
+ if (this.method == 'get')
1029
+ this.url += (this.url.include('?') ? '&' : '?') + params;
1030
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1031
+ params += '&_=';
1032
+ }
1033
+
1034
+ try {
1035
+ if (this.options.onCreate) this.options.onCreate(this.transport);
1036
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
1037
+
1038
+ this.transport.open(this.method.toUpperCase(), this.url,
1039
+ this.options.asynchronous);
1040
+
1041
+ if (this.options.asynchronous)
1042
+ setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
1043
+
1044
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
1045
+ this.setRequestHeaders();
1046
+
1047
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1048
+ this.transport.send(this.body);
1049
+
1050
+ /* Force Firefox to handle ready state 4 for synchronous requests */
1051
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
1052
+ this.onStateChange();
1053
+
1054
+ }
1055
+ catch (e) {
1056
+ this.dispatchException(e);
1057
+ }
1058
+ },
1059
+
1060
+ onStateChange: function() {
1061
+ var readyState = this.transport.readyState;
1062
+ if (readyState > 1 && !((readyState == 4) && this._complete))
1063
+ this.respondToReadyState(this.transport.readyState);
1064
+ },
1065
+
1066
+ setRequestHeaders: function() {
1067
+ var headers = {
1068
+ 'X-Requested-With': 'XMLHttpRequest',
1069
+ 'X-Prototype-Version': Prototype.Version,
1070
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1071
+ };
1072
+
1073
+ if (this.method == 'post') {
1074
+ headers['Content-type'] = this.options.contentType +
1075
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
1076
+
1077
+ /* Force "Connection: close" for older Mozilla browsers to work
1078
+ * around a bug where XMLHttpRequest sends an incorrect
1079
+ * Content-length header. See Mozilla Bugzilla #246651.
1080
+ */
1081
+ if (this.transport.overrideMimeType &&
1082
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1083
+ headers['Connection'] = 'close';
1084
+ }
1085
+
1086
+ // user-defined headers
1087
+ if (typeof this.options.requestHeaders == 'object') {
1088
+ var extras = this.options.requestHeaders;
1089
+
1090
+ if (typeof extras.push == 'function')
1091
+ for (var i = 0, length = extras.length; i < length; i += 2)
1092
+ headers[extras[i]] = extras[i+1];
1093
+ else
1094
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1095
+ }
1096
+
1097
+ for (var name in headers)
1098
+ this.transport.setRequestHeader(name, headers[name]);
1099
+ },
1100
+
1101
+ success: function() {
1102
+ return !this.transport.status
1103
+ || (this.transport.status >= 200 && this.transport.status < 300);
1104
+ },
1105
+
1106
+ respondToReadyState: function(readyState) {
1107
+ var state = Ajax.Request.Events[readyState];
1108
+ var transport = this.transport, json = this.evalJSON();
1109
+
1110
+ if (state == 'Complete') {
1111
+ try {
1112
+ this._complete = true;
1113
+ (this.options['on' + this.transport.status]
1114
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1115
+ || Prototype.emptyFunction)(transport, json);
1116
+ } catch (e) {
1117
+ this.dispatchException(e);
1118
+ }
1119
+
1120
+ var contentType = this.getHeader('Content-type');
1121
+ if (contentType && contentType.strip().
1122
+ match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
1123
+ this.evalResponse();
1124
+ }
1125
+
1126
+ try {
1127
+ (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
1128
+ Ajax.Responders.dispatch('on' + state, this, transport, json);
1129
+ } catch (e) {
1130
+ this.dispatchException(e);
1131
+ }
1132
+
1133
+ if (state == 'Complete') {
1134
+ // avoid memory leak in MSIE: clean up
1135
+ this.transport.onreadystatechange = Prototype.emptyFunction;
1136
+ }
1137
+ },
1138
+
1139
+ getHeader: function(name) {
1140
+ try {
1141
+ return this.transport.getResponseHeader(name);
1142
+ } catch (e) { return null }
1143
+ },
1144
+
1145
+ evalJSON: function() {
1146
+ try {
1147
+ var json = this.getHeader('X-JSON');
1148
+ return json ? json.evalJSON() : null;
1149
+ } catch (e) { return null }
1150
+ },
1151
+
1152
+ evalResponse: function() {
1153
+ try {
1154
+ return eval((this.transport.responseText || '').unfilterJSON());
1155
+ } catch (e) {
1156
+ this.dispatchException(e);
1157
+ }
1158
+ },
1159
+
1160
+ dispatchException: function(exception) {
1161
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
1162
+ Ajax.Responders.dispatch('onException', this, exception);
1163
+ }
1164
+ });
1165
+
1166
+ Ajax.Updater = Class.create();
1167
+
1168
+ Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
1169
+ initialize: function(container, url, options) {
1170
+ this.container = {
1171
+ success: (container.success || container),
1172
+ failure: (container.failure || (container.success ? null : container))
1173
+ }
1174
+
1175
+ this.transport = Ajax.getTransport();
1176
+ this.setOptions(options);
1177
+
1178
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
1179
+ this.options.onComplete = (function(transport, param) {
1180
+ this.updateContent();
1181
+ onComplete(transport, param);
1182
+ }).bind(this);
1183
+
1184
+ this.request(url);
1185
+ },
1186
+
1187
+ updateContent: function() {
1188
+ var receiver = this.container[this.success() ? 'success' : 'failure'];
1189
+ var response = this.transport.responseText;
1190
+
1191
+ if (!this.options.evalScripts) response = response.stripScripts();
1192
+
1193
+ if (receiver = $(receiver)) {
1194
+ if (this.options.insertion)
1195
+ new this.options.insertion(receiver, response);
1196
+ else
1197
+ receiver.update(response);
1198
+ }
1199
+
1200
+ if (this.success()) {
1201
+ if (this.onComplete)
1202
+ setTimeout(this.onComplete.bind(this), 10);
1203
+ }
1204
+ }
1205
+ });
1206
+
1207
+ Ajax.PeriodicalUpdater = Class.create();
1208
+ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1209
+ initialize: function(container, url, options) {
1210
+ this.setOptions(options);
1211
+ this.onComplete = this.options.onComplete;
1212
+
1213
+ this.frequency = (this.options.frequency || 2);
1214
+ this.decay = (this.options.decay || 1);
1215
+
1216
+ this.updater = {};
1217
+ this.container = container;
1218
+ this.url = url;
1219
+
1220
+ this.start();
1221
+ },
1222
+
1223
+ start: function() {
1224
+ this.options.onComplete = this.updateComplete.bind(this);
1225
+ this.onTimerEvent();
1226
+ },
1227
+
1228
+ stop: function() {
1229
+ this.updater.options.onComplete = undefined;
1230
+ clearTimeout(this.timer);
1231
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1232
+ },
1233
+
1234
+ updateComplete: function(request) {
1235
+ if (this.options.decay) {
1236
+ this.decay = (request.responseText == this.lastText ?
1237
+ this.decay * this.options.decay : 1);
1238
+
1239
+ this.lastText = request.responseText;
1240
+ }
1241
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
1242
+ this.decay * this.frequency * 1000);
1243
+ },
1244
+
1245
+ onTimerEvent: function() {
1246
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
1247
+ }
1248
+ });
1249
+ function $(element) {
1250
+ if (arguments.length > 1) {
1251
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1252
+ elements.push($(arguments[i]));
1253
+ return elements;
1254
+ }
1255
+ if (typeof element == 'string')
1256
+ element = document.getElementById(element);
1257
+ return Element.extend(element);
1258
+ }
1259
+
1260
+ if (Prototype.BrowserFeatures.XPath) {
1261
+ document._getElementsByXPath = function(expression, parentElement) {
1262
+ var results = [];
1263
+ var query = document.evaluate(expression, $(parentElement) || document,
1264
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1265
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
1266
+ results.push(query.snapshotItem(i));
1267
+ return results;
1268
+ };
1269
+
1270
+ document.getElementsByClassName = function(className, parentElement) {
1271
+ var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1272
+ return document._getElementsByXPath(q, parentElement);
1273
+ }
1274
+
1275
+ } else document.getElementsByClassName = function(className, parentElement) {
1276
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
1277
+ var elements = [], child, pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
1278
+ for (var i = 0, length = children.length; i < length; i++) {
1279
+ child = children[i];
1280
+ var elementClassName = child.className;
1281
+ if (elementClassName.length == 0) continue;
1282
+ if (elementClassName == className || elementClassName.match(pattern))
1283
+ elements.push(Element.extend(child));
1284
+ }
1285
+ return elements;
1286
+ };
1287
+
1288
+ /*--------------------------------------------------------------------------*/
1289
+
1290
+ if (!window.Element) var Element = {};
1291
+
1292
+ Element.extend = function(element) {
1293
+ var F = Prototype.BrowserFeatures;
1294
+ if (!element || !element.tagName || element.nodeType == 3 ||
1295
+ element._extended || F.SpecificElementExtensions || element == window)
1296
+ return element;
1297
+
1298
+ var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
1299
+ T = Element.Methods.ByTag;
1300
+
1301
+ // extend methods for all tags (Safari doesn't need this)
1302
+ if (!F.ElementExtensions) {
1303
+ Object.extend(methods, Element.Methods),
1304
+ Object.extend(methods, Element.Methods.Simulated);
1305
+ }
1306
+
1307
+ // extend methods for specific tags
1308
+ if (T[tagName]) Object.extend(methods, T[tagName]);
1309
+
1310
+ for (var property in methods) {
1311
+ var value = methods[property];
1312
+ if (typeof value == 'function' && !(property in element))
1313
+ element[property] = cache.findOrStore(value);
1314
+ }
1315
+
1316
+ element._extended = Prototype.emptyFunction;
1317
+ return element;
1318
+ };
1319
+
1320
+ Element.extend.cache = {
1321
+ findOrStore: function(value) {
1322
+ return this[value] = this[value] || function() {
1323
+ return value.apply(null, [this].concat($A(arguments)));
1324
+ }
1325
+ }
1326
+ };
1327
+
1328
+ Element.Methods = {
1329
+ visible: function(element) {
1330
+ return $(element).style.display != 'none';
1331
+ },
1332
+
1333
+ toggle: function(element) {
1334
+ element = $(element);
1335
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
1336
+ return element;
1337
+ },
1338
+
1339
+ hide: function(element) {
1340
+ $(element).style.display = 'none';
1341
+ return element;
1342
+ },
1343
+
1344
+ show: function(element) {
1345
+ $(element).style.display = '';
1346
+ return element;
1347
+ },
1348
+
1349
+ remove: function(element) {
1350
+ element = $(element);
1351
+ element.parentNode.removeChild(element);
1352
+ return element;
1353
+ },
1354
+
1355
+ update: function(element, html) {
1356
+ html = typeof html == 'undefined' ? '' : html.toString();
1357
+ $(element).innerHTML = html.stripScripts();
1358
+ setTimeout(function() {html.evalScripts()}, 10);
1359
+ return element;
1360
+ },
1361
+
1362
+ replace: function(element, html) {
1363
+ element = $(element);
1364
+ html = typeof html == 'undefined' ? '' : html.toString();
1365
+ if (element.outerHTML) {
1366
+ element.outerHTML = html.stripScripts();
1367
+ } else {
1368
+ var range = element.ownerDocument.createRange();
1369
+ range.selectNodeContents(element);
1370
+ element.parentNode.replaceChild(
1371
+ range.createContextualFragment(html.stripScripts()), element);
1372
+ }
1373
+ setTimeout(function() {html.evalScripts()}, 10);
1374
+ return element;
1375
+ },
1376
+
1377
+ inspect: function(element) {
1378
+ element = $(element);
1379
+ var result = '<' + element.tagName.toLowerCase();
1380
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1381
+ var property = pair.first(), attribute = pair.last();
1382
+ var value = (element[property] || '').toString();
1383
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
1384
+ });
1385
+ return result + '>';
1386
+ },
1387
+
1388
+ recursivelyCollect: function(element, property) {
1389
+ element = $(element);
1390
+ var elements = [];
1391
+ while (element = element[property])
1392
+ if (element.nodeType == 1)
1393
+ elements.push(Element.extend(element));
1394
+ return elements;
1395
+ },
1396
+
1397
+ ancestors: function(element) {
1398
+ return $(element).recursivelyCollect('parentNode');
1399
+ },
1400
+
1401
+ descendants: function(element) {
1402
+ return $A($(element).getElementsByTagName('*')).each(Element.extend);
1403
+ },
1404
+
1405
+ firstDescendant: function(element) {
1406
+ element = $(element).firstChild;
1407
+ while (element && element.nodeType != 1) element = element.nextSibling;
1408
+ return $(element);
1409
+ },
1410
+
1411
+ immediateDescendants: function(element) {
1412
+ if (!(element = $(element).firstChild)) return [];
1413
+ while (element && element.nodeType != 1) element = element.nextSibling;
1414
+ if (element) return [element].concat($(element).nextSiblings());
1415
+ return [];
1416
+ },
1417
+
1418
+ previousSiblings: function(element) {
1419
+ return $(element).recursivelyCollect('previousSibling');
1420
+ },
1421
+
1422
+ nextSiblings: function(element) {
1423
+ return $(element).recursivelyCollect('nextSibling');
1424
+ },
1425
+
1426
+ siblings: function(element) {
1427
+ element = $(element);
1428
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
1429
+ },
1430
+
1431
+ match: function(element, selector) {
1432
+ if (typeof selector == 'string')
1433
+ selector = new Selector(selector);
1434
+ return selector.match($(element));
1435
+ },
1436
+
1437
+ up: function(element, expression, index) {
1438
+ element = $(element);
1439
+ if (arguments.length == 1) return $(element.parentNode);
1440
+ var ancestors = element.ancestors();
1441
+ return expression ? Selector.findElement(ancestors, expression, index) :
1442
+ ancestors[index || 0];
1443
+ },
1444
+
1445
+ down: function(element, expression, index) {
1446
+ element = $(element);
1447
+ if (arguments.length == 1) return element.firstDescendant();
1448
+ var descendants = element.descendants();
1449
+ return expression ? Selector.findElement(descendants, expression, index) :
1450
+ descendants[index || 0];
1451
+ },
1452
+
1453
+ previous: function(element, expression, index) {
1454
+ element = $(element);
1455
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1456
+ var previousSiblings = element.previousSiblings();
1457
+ return expression ? Selector.findElement(previousSiblings, expression, index) :
1458
+ previousSiblings[index || 0];
1459
+ },
1460
+
1461
+ next: function(element, expression, index) {
1462
+ element = $(element);
1463
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1464
+ var nextSiblings = element.nextSiblings();
1465
+ return expression ? Selector.findElement(nextSiblings, expression, index) :
1466
+ nextSiblings[index || 0];
1467
+ },
1468
+
1469
+ getElementsBySelector: function() {
1470
+ var args = $A(arguments), element = $(args.shift());
1471
+ return Selector.findChildElements(element, args);
1472
+ },
1473
+
1474
+ getElementsByClassName: function(element, className) {
1475
+ return document.getElementsByClassName(className, element);
1476
+ },
1477
+
1478
+ readAttribute: function(element, name) {
1479
+ element = $(element);
1480
+ if (Prototype.Browser.IE) {
1481
+ if (!element.attributes) return null;
1482
+ var t = Element._attributeTranslations;
1483
+ if (t.values[name]) return t.values[name](element, name);
1484
+ if (t.names[name]) name = t.names[name];
1485
+ var attribute = element.attributes[name];
1486
+ return attribute ? attribute.nodeValue : null;
1487
+ }
1488
+ return element.getAttribute(name);
1489
+ },
1490
+
1491
+ getHeight: function(element) {
1492
+ return $(element).getDimensions().height;
1493
+ },
1494
+
1495
+ getWidth: function(element) {
1496
+ return $(element).getDimensions().width;
1497
+ },
1498
+
1499
+ classNames: function(element) {
1500
+ return new Element.ClassNames(element);
1501
+ },
1502
+
1503
+ hasClassName: function(element, className) {
1504
+ if (!(element = $(element))) return;
1505
+ var elementClassName = element.className;
1506
+ if (elementClassName.length == 0) return false;
1507
+ if (elementClassName == className ||
1508
+ elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1509
+ return true;
1510
+ return false;
1511
+ },
1512
+
1513
+ addClassName: function(element, className) {
1514
+ if (!(element = $(element))) return;
1515
+ Element.classNames(element).add(className);
1516
+ return element;
1517
+ },
1518
+
1519
+ removeClassName: function(element, className) {
1520
+ if (!(element = $(element))) return;
1521
+ Element.classNames(element).remove(className);
1522
+ return element;
1523
+ },
1524
+
1525
+ toggleClassName: function(element, className) {
1526
+ if (!(element = $(element))) return;
1527
+ Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1528
+ return element;
1529
+ },
1530
+
1531
+ observe: function() {
1532
+ Event.observe.apply(Event, arguments);
1533
+ return $A(arguments).first();
1534
+ },
1535
+
1536
+ stopObserving: function() {
1537
+ Event.stopObserving.apply(Event, arguments);
1538
+ return $A(arguments).first();
1539
+ },
1540
+
1541
+ // removes whitespace-only text node children
1542
+ cleanWhitespace: function(element) {
1543
+ element = $(element);
1544
+ var node = element.firstChild;
1545
+ while (node) {
1546
+ var nextNode = node.nextSibling;
1547
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1548
+ element.removeChild(node);
1549
+ node = nextNode;
1550
+ }
1551
+ return element;
1552
+ },
1553
+
1554
+ empty: function(element) {
1555
+ return $(element).innerHTML.blank();
1556
+ },
1557
+
1558
+ descendantOf: function(element, ancestor) {
1559
+ element = $(element), ancestor = $(ancestor);
1560
+ while (element = element.parentNode)
1561
+ if (element == ancestor) return true;
1562
+ return false;
1563
+ },
1564
+
1565
+ scrollTo: function(element) {
1566
+ element = $(element);
1567
+ var pos = Position.cumulativeOffset(element);
1568
+ window.scrollTo(pos[0], pos[1]);
1569
+ return element;
1570
+ },
1571
+
1572
+ getStyle: function(element, style) {
1573
+ element = $(element);
1574
+ style = style == 'float' ? 'cssFloat' : style.camelize();
1575
+ var value = element.style[style];
1576
+ if (!value) {
1577
+ var css = document.defaultView.getComputedStyle(element, null);
1578
+ value = css ? css[style] : null;
1579
+ }
1580
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1581
+ return value == 'auto' ? null : value;
1582
+ },
1583
+
1584
+ getOpacity: function(element) {
1585
+ return $(element).getStyle('opacity');
1586
+ },
1587
+
1588
+ setStyle: function(element, styles, camelized) {
1589
+ element = $(element);
1590
+ var elementStyle = element.style;
1591
+
1592
+ for (var property in styles)
1593
+ if (property == 'opacity') element.setOpacity(styles[property])
1594
+ else
1595
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
1596
+ (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
1597
+ (camelized ? property : property.camelize())] = styles[property];
1598
+
1599
+ return element;
1600
+ },
1601
+
1602
+ setOpacity: function(element, value) {
1603
+ element = $(element);
1604
+ element.style.opacity = (value == 1 || value === '') ? '' :
1605
+ (value < 0.00001) ? 0 : value;
1606
+ return element;
1607
+ },
1608
+
1609
+ getDimensions: function(element) {
1610
+ element = $(element);
1611
+ var display = $(element).getStyle('display');
1612
+ if (display != 'none' && display != null) // Safari bug
1613
+ return {width: element.offsetWidth, height: element.offsetHeight};
1614
+
1615
+ // All *Width and *Height properties give 0 on elements with display none,
1616
+ // so enable the element temporarily
1617
+ var els = element.style;
1618
+ var originalVisibility = els.visibility;
1619
+ var originalPosition = els.position;
1620
+ var originalDisplay = els.display;
1621
+ els.visibility = 'hidden';
1622
+ els.position = 'absolute';
1623
+ els.display = 'block';
1624
+ var originalWidth = element.clientWidth;
1625
+ var originalHeight = element.clientHeight;
1626
+ els.display = originalDisplay;
1627
+ els.position = originalPosition;
1628
+ els.visibility = originalVisibility;
1629
+ return {width: originalWidth, height: originalHeight};
1630
+ },
1631
+
1632
+ makePositioned: function(element) {
1633
+ element = $(element);
1634
+ var pos = Element.getStyle(element, 'position');
1635
+ if (pos == 'static' || !pos) {
1636
+ element._madePositioned = true;
1637
+ element.style.position = 'relative';
1638
+ // Opera returns the offset relative to the positioning context, when an
1639
+ // element is position relative but top and left have not been defined
1640
+ if (window.opera) {
1641
+ element.style.top = 0;
1642
+ element.style.left = 0;
1643
+ }
1644
+ }
1645
+ return element;
1646
+ },
1647
+
1648
+ undoPositioned: function(element) {
1649
+ element = $(element);
1650
+ if (element._madePositioned) {
1651
+ element._madePositioned = undefined;
1652
+ element.style.position =
1653
+ element.style.top =
1654
+ element.style.left =
1655
+ element.style.bottom =
1656
+ element.style.right = '';
1657
+ }
1658
+ return element;
1659
+ },
1660
+
1661
+ makeClipping: function(element) {
1662
+ element = $(element);
1663
+ if (element._overflow) return element;
1664
+ element._overflow = element.style.overflow || 'auto';
1665
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1666
+ element.style.overflow = 'hidden';
1667
+ return element;
1668
+ },
1669
+
1670
+ undoClipping: function(element) {
1671
+ element = $(element);
1672
+ if (!element._overflow) return element;
1673
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1674
+ element._overflow = null;
1675
+ return element;
1676
+ }
1677
+ };
1678
+
1679
+ Object.extend(Element.Methods, {
1680
+ childOf: Element.Methods.descendantOf,
1681
+ childElements: Element.Methods.immediateDescendants
1682
+ });
1683
+
1684
+ if (Prototype.Browser.Opera) {
1685
+ Element.Methods._getStyle = Element.Methods.getStyle;
1686
+ Element.Methods.getStyle = function(element, style) {
1687
+ switch(style) {
1688
+ case 'left':
1689
+ case 'top':
1690
+ case 'right':
1691
+ case 'bottom':
1692
+ if (Element._getStyle(element, 'position') == 'static') return null;
1693
+ default: return Element._getStyle(element, style);
1694
+ }
1695
+ };
1696
+ }
1697
+ else if (Prototype.Browser.IE) {
1698
+ Element.Methods.getStyle = function(element, style) {
1699
+ element = $(element);
1700
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
1701
+ var value = element.style[style];
1702
+ if (!value && element.currentStyle) value = element.currentStyle[style];
1703
+
1704
+ if (style == 'opacity') {
1705
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1706
+ if (value[1]) return parseFloat(value[1]) / 100;
1707
+ return 1.0;
1708
+ }
1709
+
1710
+ if (value == 'auto') {
1711
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
1712
+ return element['offset'+style.capitalize()] + 'px';
1713
+ return null;
1714
+ }
1715
+ return value;
1716
+ };
1717
+
1718
+ Element.Methods.setOpacity = function(element, value) {
1719
+ element = $(element);
1720
+ var filter = element.getStyle('filter'), style = element.style;
1721
+ if (value == 1 || value === '') {
1722
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
1723
+ return element;
1724
+ } else if (value < 0.00001) value = 0;
1725
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
1726
+ 'alpha(opacity=' + (value * 100) + ')';
1727
+ return element;
1728
+ };
1729
+
1730
+ // IE is missing .innerHTML support for TABLE-related elements
1731
+ Element.Methods.update = function(element, html) {
1732
+ element = $(element);
1733
+ html = typeof html == 'undefined' ? '' : html.toString();
1734
+ var tagName = element.tagName.toUpperCase();
1735
+ if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1736
+ var div = document.createElement('div');
1737
+ switch (tagName) {
1738
+ case 'THEAD':
1739
+ case 'TBODY':
1740
+ div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1741
+ depth = 2;
1742
+ break;
1743
+ case 'TR':
1744
+ div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1745
+ depth = 3;
1746
+ break;
1747
+ case 'TD':
1748
+ div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1749
+ depth = 4;
1750
+ }
1751
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
1752
+ depth.times(function() { div = div.firstChild });
1753
+ $A(div.childNodes).each(function(node) { element.appendChild(node) });
1754
+ } else {
1755
+ element.innerHTML = html.stripScripts();
1756
+ }
1757
+ setTimeout(function() { html.evalScripts() }, 10);
1758
+ return element;
1759
+ }
1760
+ }
1761
+ else if (Prototype.Browser.Gecko) {
1762
+ Element.Methods.setOpacity = function(element, value) {
1763
+ element = $(element);
1764
+ element.style.opacity = (value == 1) ? 0.999999 :
1765
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
1766
+ return element;
1767
+ };
1768
+ }
1769
+
1770
+ Element._attributeTranslations = {
1771
+ names: {
1772
+ colspan: "colSpan",
1773
+ rowspan: "rowSpan",
1774
+ valign: "vAlign",
1775
+ datetime: "dateTime",
1776
+ accesskey: "accessKey",
1777
+ tabindex: "tabIndex",
1778
+ enctype: "encType",
1779
+ maxlength: "maxLength",
1780
+ readonly: "readOnly",
1781
+ longdesc: "longDesc"
1782
+ },
1783
+ values: {
1784
+ _getAttr: function(element, attribute) {
1785
+ return element.getAttribute(attribute, 2);
1786
+ },
1787
+ _flag: function(element, attribute) {
1788
+ return $(element).hasAttribute(attribute) ? attribute : null;
1789
+ },
1790
+ style: function(element) {
1791
+ return element.style.cssText.toLowerCase();
1792
+ },
1793
+ title: function(element) {
1794
+ var node = element.getAttributeNode('title');
1795
+ return node.specified ? node.nodeValue : null;
1796
+ }
1797
+ }
1798
+ };
1799
+
1800
+ (function() {
1801
+ Object.extend(this, {
1802
+ href: this._getAttr,
1803
+ src: this._getAttr,
1804
+ type: this._getAttr,
1805
+ disabled: this._flag,
1806
+ checked: this._flag,
1807
+ readonly: this._flag,
1808
+ multiple: this._flag
1809
+ });
1810
+ }).call(Element._attributeTranslations.values);
1811
+
1812
+ Element.Methods.Simulated = {
1813
+ hasAttribute: function(element, attribute) {
1814
+ var t = Element._attributeTranslations, node;
1815
+ attribute = t.names[attribute] || attribute;
1816
+ node = $(element).getAttributeNode(attribute);
1817
+ return node && node.specified;
1818
+ }
1819
+ };
1820
+
1821
+ Element.Methods.ByTag = {};
1822
+
1823
+ Object.extend(Element, Element.Methods);
1824
+
1825
+ if (!Prototype.BrowserFeatures.ElementExtensions &&
1826
+ document.createElement('div').__proto__) {
1827
+ window.HTMLElement = {};
1828
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
1829
+ Prototype.BrowserFeatures.ElementExtensions = true;
1830
+ }
1831
+
1832
+ Element.hasAttribute = function(element, attribute) {
1833
+ if (element.hasAttribute) return element.hasAttribute(attribute);
1834
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
1835
+ };
1836
+
1837
+ Element.addMethods = function(methods) {
1838
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
1839
+
1840
+ if (!methods) {
1841
+ Object.extend(Form, Form.Methods);
1842
+ Object.extend(Form.Element, Form.Element.Methods);
1843
+ Object.extend(Element.Methods.ByTag, {
1844
+ "FORM": Object.clone(Form.Methods),
1845
+ "INPUT": Object.clone(Form.Element.Methods),
1846
+ "SELECT": Object.clone(Form.Element.Methods),
1847
+ "TEXTAREA": Object.clone(Form.Element.Methods)
1848
+ });
1849
+ }
1850
+
1851
+ if (arguments.length == 2) {
1852
+ var tagName = methods;
1853
+ methods = arguments[1];
1854
+ }
1855
+
1856
+ if (!tagName) Object.extend(Element.Methods, methods || {});
1857
+ else {
1858
+ if (tagName.constructor == Array) tagName.each(extend);
1859
+ else extend(tagName);
1860
+ }
1861
+
1862
+ function extend(tagName) {
1863
+ tagName = tagName.toUpperCase();
1864
+ if (!Element.Methods.ByTag[tagName])
1865
+ Element.Methods.ByTag[tagName] = {};
1866
+ Object.extend(Element.Methods.ByTag[tagName], methods);
1867
+ }
1868
+
1869
+ function copy(methods, destination, onlyIfAbsent) {
1870
+ onlyIfAbsent = onlyIfAbsent || false;
1871
+ var cache = Element.extend.cache;
1872
+ for (var property in methods) {
1873
+ var value = methods[property];
1874
+ if (!onlyIfAbsent || !(property in destination))
1875
+ destination[property] = cache.findOrStore(value);
1876
+ }
1877
+ }
1878
+
1879
+ function findDOMClass(tagName) {
1880
+ var klass;
1881
+ var trans = {
1882
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
1883
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
1884
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
1885
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
1886
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
1887
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
1888
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
1889
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
1890
+ "FrameSet", "IFRAME": "IFrame"
1891
+ };
1892
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
1893
+ if (window[klass]) return window[klass];
1894
+ klass = 'HTML' + tagName + 'Element';
1895
+ if (window[klass]) return window[klass];
1896
+ klass = 'HTML' + tagName.capitalize() + 'Element';
1897
+ if (window[klass]) return window[klass];
1898
+
1899
+ window[klass] = {};
1900
+ window[klass].prototype = document.createElement(tagName).__proto__;
1901
+ return window[klass];
1902
+ }
1903
+
1904
+ if (F.ElementExtensions) {
1905
+ copy(Element.Methods, HTMLElement.prototype);
1906
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1907
+ }
1908
+
1909
+ if (F.SpecificElementExtensions) {
1910
+ for (var tag in Element.Methods.ByTag) {
1911
+ var klass = findDOMClass(tag);
1912
+ if (typeof klass == "undefined") continue;
1913
+ copy(T[tag], klass.prototype);
1914
+ }
1915
+ }
1916
+
1917
+ Object.extend(Element, Element.Methods);
1918
+ delete Element.ByTag;
1919
+ };
1920
+
1921
+ var Toggle = { display: Element.toggle };
1922
+
1923
+ /*--------------------------------------------------------------------------*/
1924
+
1925
+ Abstract.Insertion = function(adjacency) {
1926
+ this.adjacency = adjacency;
1927
+ }
1928
+
1929
+ Abstract.Insertion.prototype = {
1930
+ initialize: function(element, content) {
1931
+ this.element = $(element);
1932
+ this.content = content.stripScripts();
1933
+
1934
+ if (this.adjacency && this.element.insertAdjacentHTML) {
1935
+ try {
1936
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
1937
+ } catch (e) {
1938
+ var tagName = this.element.tagName.toUpperCase();
1939
+ if (['TBODY', 'TR'].include(tagName)) {
1940
+ this.insertContent(this.contentFromAnonymousTable());
1941
+ } else {
1942
+ throw e;
1943
+ }
1944
+ }
1945
+ } else {
1946
+ this.range = this.element.ownerDocument.createRange();
1947
+ if (this.initializeRange) this.initializeRange();
1948
+ this.insertContent([this.range.createContextualFragment(this.content)]);
1949
+ }
1950
+
1951
+ setTimeout(function() {content.evalScripts()}, 10);
1952
+ },
1953
+
1954
+ contentFromAnonymousTable: function() {
1955
+ var div = document.createElement('div');
1956
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1957
+ return $A(div.childNodes[0].childNodes[0].childNodes);
1958
+ }
1959
+ }
1960
+
1961
+ var Insertion = new Object();
1962
+
1963
+ Insertion.Before = Class.create();
1964
+ Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1965
+ initializeRange: function() {
1966
+ this.range.setStartBefore(this.element);
1967
+ },
1968
+
1969
+ insertContent: function(fragments) {
1970
+ fragments.each((function(fragment) {
1971
+ this.element.parentNode.insertBefore(fragment, this.element);
1972
+ }).bind(this));
1973
+ }
1974
+ });
1975
+
1976
+ Insertion.Top = Class.create();
1977
+ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1978
+ initializeRange: function() {
1979
+ this.range.selectNodeContents(this.element);
1980
+ this.range.collapse(true);
1981
+ },
1982
+
1983
+ insertContent: function(fragments) {
1984
+ fragments.reverse(false).each((function(fragment) {
1985
+ this.element.insertBefore(fragment, this.element.firstChild);
1986
+ }).bind(this));
1987
+ }
1988
+ });
1989
+
1990
+ Insertion.Bottom = Class.create();
1991
+ Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1992
+ initializeRange: function() {
1993
+ this.range.selectNodeContents(this.element);
1994
+ this.range.collapse(this.element);
1995
+ },
1996
+
1997
+ insertContent: function(fragments) {
1998
+ fragments.each((function(fragment) {
1999
+ this.element.appendChild(fragment);
2000
+ }).bind(this));
2001
+ }
2002
+ });
2003
+
2004
+ Insertion.After = Class.create();
2005
+ Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
2006
+ initializeRange: function() {
2007
+ this.range.setStartAfter(this.element);
2008
+ },
2009
+
2010
+ insertContent: function(fragments) {
2011
+ fragments.each((function(fragment) {
2012
+ this.element.parentNode.insertBefore(fragment,
2013
+ this.element.nextSibling);
2014
+ }).bind(this));
2015
+ }
2016
+ });
2017
+
2018
+ /*--------------------------------------------------------------------------*/
2019
+
2020
+ Element.ClassNames = Class.create();
2021
+ Element.ClassNames.prototype = {
2022
+ initialize: function(element) {
2023
+ this.element = $(element);
2024
+ },
2025
+
2026
+ _each: function(iterator) {
2027
+ this.element.className.split(/\s+/).select(function(name) {
2028
+ return name.length > 0;
2029
+ })._each(iterator);
2030
+ },
2031
+
2032
+ set: function(className) {
2033
+ this.element.className = className;
2034
+ },
2035
+
2036
+ add: function(classNameToAdd) {
2037
+ if (this.include(classNameToAdd)) return;
2038
+ this.set($A(this).concat(classNameToAdd).join(' '));
2039
+ },
2040
+
2041
+ remove: function(classNameToRemove) {
2042
+ if (!this.include(classNameToRemove)) return;
2043
+ this.set($A(this).without(classNameToRemove).join(' '));
2044
+ },
2045
+
2046
+ toString: function() {
2047
+ return $A(this).join(' ');
2048
+ }
2049
+ };
2050
+
2051
+ Object.extend(Element.ClassNames.prototype, Enumerable);
2052
+ /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2053
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2054
+ * license. Please see http://www.yui-ext.com/ for more information. */
2055
+
2056
+ var Selector = Class.create();
2057
+
2058
+ Selector.prototype = {
2059
+ initialize: function(expression) {
2060
+ this.expression = expression.strip();
2061
+ this.compileMatcher();
2062
+ },
2063
+
2064
+ compileMatcher: function() {
2065
+ // Selectors with namespaced attributes can't use the XPath version
2066
+ if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
2067
+ return this.compileXPathMatcher();
2068
+
2069
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2070
+ c = Selector.criteria, le, p, m;
2071
+
2072
+ if (Selector._cache[e]) {
2073
+ this.matcher = Selector._cache[e]; return;
2074
+ }
2075
+ this.matcher = ["this.matcher = function(root) {",
2076
+ "var r = root, h = Selector.handlers, c = false, n;"];
2077
+
2078
+ while (e && le != e && (/\S/).test(e)) {
2079
+ le = e;
2080
+ for (var i in ps) {
2081
+ p = ps[i];
2082
+ if (m = e.match(p)) {
2083
+ this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
2084
+ new Template(c[i]).evaluate(m));
2085
+ e = e.replace(m[0], '');
2086
+ break;
2087
+ }
2088
+ }
2089
+ }
2090
+
2091
+ this.matcher.push("return h.unique(n);\n}");
2092
+ eval(this.matcher.join('\n'));
2093
+ Selector._cache[this.expression] = this.matcher;
2094
+ },
2095
+
2096
+ compileXPathMatcher: function() {
2097
+ var e = this.expression, ps = Selector.patterns,
2098
+ x = Selector.xpath, le, m;
2099
+
2100
+ if (Selector._cache[e]) {
2101
+ this.xpath = Selector._cache[e]; return;
2102
+ }
2103
+
2104
+ this.matcher = ['.//*'];
2105
+ while (e && le != e && (/\S/).test(e)) {
2106
+ le = e;
2107
+ for (var i in ps) {
2108
+ if (m = e.match(ps[i])) {
2109
+ this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
2110
+ new Template(x[i]).evaluate(m));
2111
+ e = e.replace(m[0], '');
2112
+ break;
2113
+ }
2114
+ }
2115
+ }
2116
+
2117
+ this.xpath = this.matcher.join('');
2118
+ Selector._cache[this.expression] = this.xpath;
2119
+ },
2120
+
2121
+ findElements: function(root) {
2122
+ root = root || document;
2123
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2124
+ return this.matcher(root);
2125
+ },
2126
+
2127
+ match: function(element) {
2128
+ return this.findElements(document).include(element);
2129
+ },
2130
+
2131
+ toString: function() {
2132
+ return this.expression;
2133
+ },
2134
+
2135
+ inspect: function() {
2136
+ return "#<Selector:" + this.expression.inspect() + ">";
2137
+ }
2138
+ };
2139
+
2140
+ Object.extend(Selector, {
2141
+ _cache: {},
2142
+
2143
+ xpath: {
2144
+ descendant: "//*",
2145
+ child: "/*",
2146
+ adjacent: "/following-sibling::*[1]",
2147
+ laterSibling: '/following-sibling::*',
2148
+ tagName: function(m) {
2149
+ if (m[1] == '*') return '';
2150
+ return "[local-name()='" + m[1].toLowerCase() +
2151
+ "' or local-name()='" + m[1].toUpperCase() + "']";
2152
+ },
2153
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2154
+ id: "[@id='#{1}']",
2155
+ attrPresence: "[@#{1}]",
2156
+ attr: function(m) {
2157
+ m[3] = m[5] || m[6];
2158
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2159
+ },
2160
+ pseudo: function(m) {
2161
+ var h = Selector.xpath.pseudos[m[1]];
2162
+ if (!h) return '';
2163
+ if (typeof h === 'function') return h(m);
2164
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2165
+ },
2166
+ operators: {
2167
+ '=': "[@#{1}='#{3}']",
2168
+ '!=': "[@#{1}!='#{3}']",
2169
+ '^=': "[starts-with(@#{1}, '#{3}')]",
2170
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2171
+ '*=': "[contains(@#{1}, '#{3}')]",
2172
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2173
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2174
+ },
2175
+ pseudos: {
2176
+ 'first-child': '[not(preceding-sibling::*)]',
2177
+ 'last-child': '[not(following-sibling::*)]',
2178
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2179
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2180
+ 'checked': "[@checked]",
2181
+ 'disabled': "[@disabled]",
2182
+ 'enabled': "[not(@disabled)]",
2183
+ 'not': function(m) {
2184
+ var e = m[6], p = Selector.patterns,
2185
+ x = Selector.xpath, le, m, v;
2186
+
2187
+ var exclusion = [];
2188
+ while (e && le != e && (/\S/).test(e)) {
2189
+ le = e;
2190
+ for (var i in p) {
2191
+ if (m = e.match(p[i])) {
2192
+ v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
2193
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2194
+ e = e.replace(m[0], '');
2195
+ break;
2196
+ }
2197
+ }
2198
+ }
2199
+ return "[not(" + exclusion.join(" and ") + ")]";
2200
+ },
2201
+ 'nth-child': function(m) {
2202
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2203
+ },
2204
+ 'nth-last-child': function(m) {
2205
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2206
+ },
2207
+ 'nth-of-type': function(m) {
2208
+ return Selector.xpath.pseudos.nth("position() ", m);
2209
+ },
2210
+ 'nth-last-of-type': function(m) {
2211
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2212
+ },
2213
+ 'first-of-type': function(m) {
2214
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2215
+ },
2216
+ 'last-of-type': function(m) {
2217
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2218
+ },
2219
+ 'only-of-type': function(m) {
2220
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2221
+ },
2222
+ nth: function(fragment, m) {
2223
+ var mm, formula = m[6], predicate;
2224
+ if (formula == 'even') formula = '2n+0';
2225
+ if (formula == 'odd') formula = '2n+1';
2226
+ if (mm = formula.match(/^(\d+)$/)) // digit only
2227
+ return '[' + fragment + "= " + mm[1] + ']';
2228
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2229
+ if (mm[1] == "-") mm[1] = -1;
2230
+ var a = mm[1] ? Number(mm[1]) : 1;
2231
+ var b = mm[2] ? Number(mm[2]) : 0;
2232
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2233
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
2234
+ return new Template(predicate).evaluate({
2235
+ fragment: fragment, a: a, b: b });
2236
+ }
2237
+ }
2238
+ }
2239
+ },
2240
+
2241
+ criteria: {
2242
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2243
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
2244
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
2245
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2246
+ attr: function(m) {
2247
+ m[3] = (m[5] || m[6]);
2248
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2249
+ },
2250
+ pseudo: function(m) {
2251
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2252
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2253
+ },
2254
+ descendant: 'c = "descendant";',
2255
+ child: 'c = "child";',
2256
+ adjacent: 'c = "adjacent";',
2257
+ laterSibling: 'c = "laterSibling";'
2258
+ },
2259
+
2260
+ patterns: {
2261
+ // combinators must be listed first
2262
+ // (and descendant needs to be last combinator)
2263
+ laterSibling: /^\s*~\s*/,
2264
+ child: /^\s*>\s*/,
2265
+ adjacent: /^\s*\+\s*/,
2266
+ descendant: /^\s/,
2267
+
2268
+ // selectors follow
2269
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2270
+ id: /^#([\w\-\*]+)(\b|$)/,
2271
+ className: /^\.([\w\-\*]+)(\b|$)/,
2272
+ pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
2273
+ attrPresence: /^\[([\w]+)\]/,
2274
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
2275
+ },
2276
+
2277
+ handlers: {
2278
+ // UTILITY FUNCTIONS
2279
+ // joins two collections
2280
+ concat: function(a, b) {
2281
+ for (var i = 0, node; node = b[i]; i++)
2282
+ a.push(node);
2283
+ return a;
2284
+ },
2285
+
2286
+ // marks an array of nodes for counting
2287
+ mark: function(nodes) {
2288
+ for (var i = 0, node; node = nodes[i]; i++)
2289
+ node._counted = true;
2290
+ return nodes;
2291
+ },
2292
+
2293
+ unmark: function(nodes) {
2294
+ for (var i = 0, node; node = nodes[i]; i++)
2295
+ node._counted = undefined;
2296
+ return nodes;
2297
+ },
2298
+
2299
+ // mark each child node with its position (for nth calls)
2300
+ // "ofType" flag indicates whether we're indexing for nth-of-type
2301
+ // rather than nth-child
2302
+ index: function(parentNode, reverse, ofType) {
2303
+ parentNode._counted = true;
2304
+ if (reverse) {
2305
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
2306
+ node = nodes[i];
2307
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2308
+ }
2309
+ } else {
2310
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
2311
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2312
+ }
2313
+ },
2314
+
2315
+ // filters out duplicates and extends all nodes
2316
+ unique: function(nodes) {
2317
+ if (nodes.length == 0) return nodes;
2318
+ var results = [], n;
2319
+ for (var i = 0, l = nodes.length; i < l; i++)
2320
+ if (!(n = nodes[i])._counted) {
2321
+ n._counted = true;
2322
+ results.push(Element.extend(n));
2323
+ }
2324
+ return Selector.handlers.unmark(results);
2325
+ },
2326
+
2327
+ // COMBINATOR FUNCTIONS
2328
+ descendant: function(nodes) {
2329
+ var h = Selector.handlers;
2330
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2331
+ h.concat(results, node.getElementsByTagName('*'));
2332
+ return results;
2333
+ },
2334
+
2335
+ child: function(nodes) {
2336
+ var h = Selector.handlers;
2337
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2338
+ for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
2339
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
2340
+ }
2341
+ return results;
2342
+ },
2343
+
2344
+ adjacent: function(nodes) {
2345
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2346
+ var next = this.nextElementSibling(node);
2347
+ if (next) results.push(next);
2348
+ }
2349
+ return results;
2350
+ },
2351
+
2352
+ laterSibling: function(nodes) {
2353
+ var h = Selector.handlers;
2354
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2355
+ h.concat(results, Element.nextSiblings(node));
2356
+ return results;
2357
+ },
2358
+
2359
+ nextElementSibling: function(node) {
2360
+ while (node = node.nextSibling)
2361
+ if (node.nodeType == 1) return node;
2362
+ return null;
2363
+ },
2364
+
2365
+ previousElementSibling: function(node) {
2366
+ while (node = node.previousSibling)
2367
+ if (node.nodeType == 1) return node;
2368
+ return null;
2369
+ },
2370
+
2371
+ // TOKEN FUNCTIONS
2372
+ tagName: function(nodes, root, tagName, combinator) {
2373
+ tagName = tagName.toUpperCase();
2374
+ var results = [], h = Selector.handlers;
2375
+ if (nodes) {
2376
+ if (combinator) {
2377
+ // fastlane for ordinary descendant combinators
2378
+ if (combinator == "descendant") {
2379
+ for (var i = 0, node; node = nodes[i]; i++)
2380
+ h.concat(results, node.getElementsByTagName(tagName));
2381
+ return results;
2382
+ } else nodes = this[combinator](nodes);
2383
+ if (tagName == "*") return nodes;
2384
+ }
2385
+ for (var i = 0, node; node = nodes[i]; i++)
2386
+ if (node.tagName.toUpperCase() == tagName) results.push(node);
2387
+ return results;
2388
+ } else return root.getElementsByTagName(tagName);
2389
+ },
2390
+
2391
+ id: function(nodes, root, id, combinator) {
2392
+ var targetNode = $(id), h = Selector.handlers;
2393
+ if (!nodes && root == document) return targetNode ? [targetNode] : [];
2394
+ if (nodes) {
2395
+ if (combinator) {
2396
+ if (combinator == 'child') {
2397
+ for (var i = 0, node; node = nodes[i]; i++)
2398
+ if (targetNode.parentNode == node) return [targetNode];
2399
+ } else if (combinator == 'descendant') {
2400
+ for (var i = 0, node; node = nodes[i]; i++)
2401
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
2402
+ } else if (combinator == 'adjacent') {
2403
+ for (var i = 0, node; node = nodes[i]; i++)
2404
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
2405
+ return [targetNode];
2406
+ } else nodes = h[combinator](nodes);
2407
+ }
2408
+ for (var i = 0, node; node = nodes[i]; i++)
2409
+ if (node == targetNode) return [targetNode];
2410
+ return [];
2411
+ }
2412
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
2413
+ },
2414
+
2415
+ className: function(nodes, root, className, combinator) {
2416
+ if (nodes && combinator) nodes = this[combinator](nodes);
2417
+ return Selector.handlers.byClassName(nodes, root, className);
2418
+ },
2419
+
2420
+ byClassName: function(nodes, root, className) {
2421
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
2422
+ var needle = ' ' + className + ' ';
2423
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
2424
+ nodeClassName = node.className;
2425
+ if (nodeClassName.length == 0) continue;
2426
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
2427
+ results.push(node);
2428
+ }
2429
+ return results;
2430
+ },
2431
+
2432
+ attrPresence: function(nodes, root, attr) {
2433
+ var results = [];
2434
+ for (var i = 0, node; node = nodes[i]; i++)
2435
+ if (Element.hasAttribute(node, attr)) results.push(node);
2436
+ return results;
2437
+ },
2438
+
2439
+ attr: function(nodes, root, attr, value, operator) {
2440
+ if (!nodes) nodes = root.getElementsByTagName("*");
2441
+ var handler = Selector.operators[operator], results = [];
2442
+ for (var i = 0, node; node = nodes[i]; i++) {
2443
+ var nodeValue = Element.readAttribute(node, attr);
2444
+ if (nodeValue === null) continue;
2445
+ if (handler(nodeValue, value)) results.push(node);
2446
+ }
2447
+ return results;
2448
+ },
2449
+
2450
+ pseudo: function(nodes, name, value, root, combinator) {
2451
+ if (nodes && combinator) nodes = this[combinator](nodes);
2452
+ if (!nodes) nodes = root.getElementsByTagName("*");
2453
+ return Selector.pseudos[name](nodes, value, root);
2454
+ }
2455
+ },
2456
+
2457
+ pseudos: {
2458
+ 'first-child': function(nodes, value, root) {
2459
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2460
+ if (Selector.handlers.previousElementSibling(node)) continue;
2461
+ results.push(node);
2462
+ }
2463
+ return results;
2464
+ },
2465
+ 'last-child': function(nodes, value, root) {
2466
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2467
+ if (Selector.handlers.nextElementSibling(node)) continue;
2468
+ results.push(node);
2469
+ }
2470
+ return results;
2471
+ },
2472
+ 'only-child': function(nodes, value, root) {
2473
+ var h = Selector.handlers;
2474
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2475
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
2476
+ results.push(node);
2477
+ return results;
2478
+ },
2479
+ 'nth-child': function(nodes, formula, root) {
2480
+ return Selector.pseudos.nth(nodes, formula, root);
2481
+ },
2482
+ 'nth-last-child': function(nodes, formula, root) {
2483
+ return Selector.pseudos.nth(nodes, formula, root, true);
2484
+ },
2485
+ 'nth-of-type': function(nodes, formula, root) {
2486
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
2487
+ },
2488
+ 'nth-last-of-type': function(nodes, formula, root) {
2489
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
2490
+ },
2491
+ 'first-of-type': function(nodes, formula, root) {
2492
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
2493
+ },
2494
+ 'last-of-type': function(nodes, formula, root) {
2495
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
2496
+ },
2497
+ 'only-of-type': function(nodes, formula, root) {
2498
+ var p = Selector.pseudos;
2499
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
2500
+ },
2501
+
2502
+ // handles the an+b logic
2503
+ getIndices: function(a, b, total) {
2504
+ if (a == 0) return b > 0 ? [b] : [];
2505
+ return $R(1, total).inject([], function(memo, i) {
2506
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
2507
+ return memo;
2508
+ });
2509
+ },
2510
+
2511
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
2512
+ nth: function(nodes, formula, root, reverse, ofType) {
2513
+ if (nodes.length == 0) return [];
2514
+ if (formula == 'even') formula = '2n+0';
2515
+ if (formula == 'odd') formula = '2n+1';
2516
+ var h = Selector.handlers, results = [], indexed = [], m;
2517
+ h.mark(nodes);
2518
+ for (var i = 0, node; node = nodes[i]; i++) {
2519
+ if (!node.parentNode._counted) {
2520
+ h.index(node.parentNode, reverse, ofType);
2521
+ indexed.push(node.parentNode);
2522
+ }
2523
+ }
2524
+ if (formula.match(/^\d+$/)) { // just a number
2525
+ formula = Number(formula);
2526
+ for (var i = 0, node; node = nodes[i]; i++)
2527
+ if (node.nodeIndex == formula) results.push(node);
2528
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2529
+ if (m[1] == "-") m[1] = -1;
2530
+ var a = m[1] ? Number(m[1]) : 1;
2531
+ var b = m[2] ? Number(m[2]) : 0;
2532
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
2533
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
2534
+ for (var j = 0; j < l; j++)
2535
+ if (node.nodeIndex == indices[j]) results.push(node);
2536
+ }
2537
+ }
2538
+ h.unmark(nodes);
2539
+ h.unmark(indexed);
2540
+ return results;
2541
+ },
2542
+
2543
+ 'empty': function(nodes, value, root) {
2544
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
2545
+ // IE treats comments as element nodes
2546
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
2547
+ results.push(node);
2548
+ }
2549
+ return results;
2550
+ },
2551
+
2552
+ 'not': function(nodes, selector, root) {
2553
+ var h = Selector.handlers, selectorType, m;
2554
+ var exclusions = new Selector(selector).findElements(root);
2555
+ h.mark(exclusions);
2556
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2557
+ if (!node._counted) results.push(node);
2558
+ h.unmark(exclusions);
2559
+ return results;
2560
+ },
2561
+
2562
+ 'enabled': function(nodes, value, root) {
2563
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2564
+ if (!node.disabled) results.push(node);
2565
+ return results;
2566
+ },
2567
+
2568
+ 'disabled': function(nodes, value, root) {
2569
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2570
+ if (node.disabled) results.push(node);
2571
+ return results;
2572
+ },
2573
+
2574
+ 'checked': function(nodes, value, root) {
2575
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
2576
+ if (node.checked) results.push(node);
2577
+ return results;
2578
+ }
2579
+ },
2580
+
2581
+ operators: {
2582
+ '=': function(nv, v) { return nv == v; },
2583
+ '!=': function(nv, v) { return nv != v; },
2584
+ '^=': function(nv, v) { return nv.startsWith(v); },
2585
+ '$=': function(nv, v) { return nv.endsWith(v); },
2586
+ '*=': function(nv, v) { return nv.include(v); },
2587
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
2588
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
2589
+ },
2590
+
2591
+ matchElements: function(elements, expression) {
2592
+ var matches = new Selector(expression).findElements(), h = Selector.handlers;
2593
+ h.mark(matches);
2594
+ for (var i = 0, results = [], element; element = elements[i]; i++)
2595
+ if (element._counted) results.push(element);
2596
+ h.unmark(matches);
2597
+ return results;
2598
+ },
2599
+
2600
+ findElement: function(elements, expression, index) {
2601
+ if (typeof expression == 'number') {
2602
+ index = expression; expression = false;
2603
+ }
2604
+ return Selector.matchElements(elements, expression || '*')[index || 0];
2605
+ },
2606
+
2607
+ findChildElements: function(element, expressions) {
2608
+ var exprs = expressions.join(','), expressions = [];
2609
+ exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
2610
+ expressions.push(m[1].strip());
2611
+ });
2612
+ var results = [], h = Selector.handlers;
2613
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
2614
+ selector = new Selector(expressions[i].strip());
2615
+ h.concat(results, selector.findElements(element));
2616
+ }
2617
+ return (l > 1) ? h.unique(results) : results;
2618
+ }
2619
+ });
2620
+
2621
+ function $$() {
2622
+ return Selector.findChildElements(document, $A(arguments));
2623
+ }
2624
+ var Form = {
2625
+ reset: function(form) {
2626
+ $(form).reset();
2627
+ return form;
2628
+ },
2629
+
2630
+ serializeElements: function(elements, getHash) {
2631
+ var data = elements.inject({}, function(result, element) {
2632
+ if (!element.disabled && element.name) {
2633
+ var key = element.name, value = $(element).getValue();
2634
+ if (value != null) {
2635
+ if (key in result) {
2636
+ if (result[key].constructor != Array) result[key] = [result[key]];
2637
+ result[key].push(value);
2638
+ }
2639
+ else result[key] = value;
2640
+ }
2641
+ }
2642
+ return result;
2643
+ });
2644
+
2645
+ return getHash ? data : Hash.toQueryString(data);
2646
+ }
2647
+ };
2648
+
2649
+ Form.Methods = {
2650
+ serialize: function(form, getHash) {
2651
+ return Form.serializeElements(Form.getElements(form), getHash);
2652
+ },
2653
+
2654
+ getElements: function(form) {
2655
+ return $A($(form).getElementsByTagName('*')).inject([],
2656
+ function(elements, child) {
2657
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
2658
+ elements.push(Element.extend(child));
2659
+ return elements;
2660
+ }
2661
+ );
2662
+ },
2663
+
2664
+ getInputs: function(form, typeName, name) {
2665
+ form = $(form);
2666
+ var inputs = form.getElementsByTagName('input');
2667
+
2668
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
2669
+
2670
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
2671
+ var input = inputs[i];
2672
+ if ((typeName && input.type != typeName) || (name && input.name != name))
2673
+ continue;
2674
+ matchingInputs.push(Element.extend(input));
2675
+ }
2676
+
2677
+ return matchingInputs;
2678
+ },
2679
+
2680
+ disable: function(form) {
2681
+ form = $(form);
2682
+ Form.getElements(form).invoke('disable');
2683
+ return form;
2684
+ },
2685
+
2686
+ enable: function(form) {
2687
+ form = $(form);
2688
+ Form.getElements(form).invoke('enable');
2689
+ return form;
2690
+ },
2691
+
2692
+ findFirstElement: function(form) {
2693
+ return $(form).getElements().find(function(element) {
2694
+ return element.type != 'hidden' && !element.disabled &&
2695
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
2696
+ });
2697
+ },
2698
+
2699
+ focusFirstElement: function(form) {
2700
+ form = $(form);
2701
+ form.findFirstElement().activate();
2702
+ return form;
2703
+ },
2704
+
2705
+ request: function(form, options) {
2706
+ form = $(form), options = Object.clone(options || {});
2707
+
2708
+ var params = options.parameters;
2709
+ options.parameters = form.serialize(true);
2710
+
2711
+ if (params) {
2712
+ if (typeof params == 'string') params = params.toQueryParams();
2713
+ Object.extend(options.parameters, params);
2714
+ }
2715
+
2716
+ if (form.hasAttribute('method') && !options.method)
2717
+ options.method = form.method;
2718
+
2719
+ return new Ajax.Request(form.readAttribute('action'), options);
2720
+ }
2721
+ }
2722
+
2723
+ /*--------------------------------------------------------------------------*/
2724
+
2725
+ Form.Element = {
2726
+ focus: function(element) {
2727
+ $(element).focus();
2728
+ return element;
2729
+ },
2730
+
2731
+ select: function(element) {
2732
+ $(element).select();
2733
+ return element;
2734
+ }
2735
+ }
2736
+
2737
+ Form.Element.Methods = {
2738
+ serialize: function(element) {
2739
+ element = $(element);
2740
+ if (!element.disabled && element.name) {
2741
+ var value = element.getValue();
2742
+ if (value != undefined) {
2743
+ var pair = {};
2744
+ pair[element.name] = value;
2745
+ return Hash.toQueryString(pair);
2746
+ }
2747
+ }
2748
+ return '';
2749
+ },
2750
+
2751
+ getValue: function(element) {
2752
+ element = $(element);
2753
+ var method = element.tagName.toLowerCase();
2754
+ return Form.Element.Serializers[method](element);
2755
+ },
2756
+
2757
+ clear: function(element) {
2758
+ $(element).value = '';
2759
+ return element;
2760
+ },
2761
+
2762
+ present: function(element) {
2763
+ return $(element).value != '';
2764
+ },
2765
+
2766
+ activate: function(element) {
2767
+ element = $(element);
2768
+ try {
2769
+ element.focus();
2770
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
2771
+ !['button', 'reset', 'submit'].include(element.type)))
2772
+ element.select();
2773
+ } catch (e) {}
2774
+ return element;
2775
+ },
2776
+
2777
+ disable: function(element) {
2778
+ element = $(element);
2779
+ element.blur();
2780
+ element.disabled = true;
2781
+ return element;
2782
+ },
2783
+
2784
+ enable: function(element) {
2785
+ element = $(element);
2786
+ element.disabled = false;
2787
+ return element;
2788
+ }
2789
+ }
2790
+
2791
+ /*--------------------------------------------------------------------------*/
2792
+
2793
+ var Field = Form.Element;
2794
+ var $F = Form.Element.Methods.getValue;
2795
+
2796
+ /*--------------------------------------------------------------------------*/
2797
+
2798
+ Form.Element.Serializers = {
2799
+ input: function(element) {
2800
+ switch (element.type.toLowerCase()) {
2801
+ case 'checkbox':
2802
+ case 'radio':
2803
+ return Form.Element.Serializers.inputSelector(element);
2804
+ default:
2805
+ return Form.Element.Serializers.textarea(element);
2806
+ }
2807
+ },
2808
+
2809
+ inputSelector: function(element) {
2810
+ return element.checked ? element.value : null;
2811
+ },
2812
+
2813
+ textarea: function(element) {
2814
+ return element.value;
2815
+ },
2816
+
2817
+ select: function(element) {
2818
+ return this[element.type == 'select-one' ?
2819
+ 'selectOne' : 'selectMany'](element);
2820
+ },
2821
+
2822
+ selectOne: function(element) {
2823
+ var index = element.selectedIndex;
2824
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
2825
+ },
2826
+
2827
+ selectMany: function(element) {
2828
+ var values, length = element.length;
2829
+ if (!length) return null;
2830
+
2831
+ for (var i = 0, values = []; i < length; i++) {
2832
+ var opt = element.options[i];
2833
+ if (opt.selected) values.push(this.optionValue(opt));
2834
+ }
2835
+ return values;
2836
+ },
2837
+
2838
+ optionValue: function(opt) {
2839
+ // extend element because hasAttribute may not be native
2840
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2841
+ }
2842
+ }
2843
+
2844
+ /*--------------------------------------------------------------------------*/
2845
+
2846
+ Abstract.TimedObserver = function() {}
2847
+ Abstract.TimedObserver.prototype = {
2848
+ initialize: function(element, frequency, callback) {
2849
+ this.frequency = frequency;
2850
+ this.element = $(element);
2851
+ this.callback = callback;
2852
+
2853
+ this.lastValue = this.getValue();
2854
+ this.registerCallback();
2855
+ },
2856
+
2857
+ registerCallback: function() {
2858
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2859
+ },
2860
+
2861
+ onTimerEvent: function() {
2862
+ var value = this.getValue();
2863
+ var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2864
+ ? this.lastValue != value : String(this.lastValue) != String(value));
2865
+ if (changed) {
2866
+ this.callback(this.element, value);
2867
+ this.lastValue = value;
2868
+ }
2869
+ }
2870
+ }
2871
+
2872
+ Form.Element.Observer = Class.create();
2873
+ Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2874
+ getValue: function() {
2875
+ return Form.Element.getValue(this.element);
2876
+ }
2877
+ });
2878
+
2879
+ Form.Observer = Class.create();
2880
+ Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2881
+ getValue: function() {
2882
+ return Form.serialize(this.element);
2883
+ }
2884
+ });
2885
+
2886
+ /*--------------------------------------------------------------------------*/
2887
+
2888
+ Abstract.EventObserver = function() {}
2889
+ Abstract.EventObserver.prototype = {
2890
+ initialize: function(element, callback) {
2891
+ this.element = $(element);
2892
+ this.callback = callback;
2893
+
2894
+ this.lastValue = this.getValue();
2895
+ if (this.element.tagName.toLowerCase() == 'form')
2896
+ this.registerFormCallbacks();
2897
+ else
2898
+ this.registerCallback(this.element);
2899
+ },
2900
+
2901
+ onElementEvent: function() {
2902
+ var value = this.getValue();
2903
+ if (this.lastValue != value) {
2904
+ this.callback(this.element, value);
2905
+ this.lastValue = value;
2906
+ }
2907
+ },
2908
+
2909
+ registerFormCallbacks: function() {
2910
+ Form.getElements(this.element).each(this.registerCallback.bind(this));
2911
+ },
2912
+
2913
+ registerCallback: function(element) {
2914
+ if (element.type) {
2915
+ switch (element.type.toLowerCase()) {
2916
+ case 'checkbox':
2917
+ case 'radio':
2918
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
2919
+ break;
2920
+ default:
2921
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
2922
+ break;
2923
+ }
2924
+ }
2925
+ }
2926
+ }
2927
+
2928
+ Form.Element.EventObserver = Class.create();
2929
+ Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2930
+ getValue: function() {
2931
+ return Form.Element.getValue(this.element);
2932
+ }
2933
+ });
2934
+
2935
+ Form.EventObserver = Class.create();
2936
+ Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2937
+ getValue: function() {
2938
+ return Form.serialize(this.element);
2939
+ }
2940
+ });
2941
+ if (!window.Event) {
2942
+ var Event = new Object();
2943
+ }
2944
+
2945
+ Object.extend(Event, {
2946
+ KEY_BACKSPACE: 8,
2947
+ KEY_TAB: 9,
2948
+ KEY_RETURN: 13,
2949
+ KEY_ESC: 27,
2950
+ KEY_LEFT: 37,
2951
+ KEY_UP: 38,
2952
+ KEY_RIGHT: 39,
2953
+ KEY_DOWN: 40,
2954
+ KEY_DELETE: 46,
2955
+ KEY_HOME: 36,
2956
+ KEY_END: 35,
2957
+ KEY_PAGEUP: 33,
2958
+ KEY_PAGEDOWN: 34,
2959
+
2960
+ element: function(event) {
2961
+ return $(event.target || event.srcElement);
2962
+ },
2963
+
2964
+ isLeftClick: function(event) {
2965
+ return (((event.which) && (event.which == 1)) ||
2966
+ ((event.button) && (event.button == 1)));
2967
+ },
2968
+
2969
+ pointerX: function(event) {
2970
+ return event.pageX || (event.clientX +
2971
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
2972
+ },
2973
+
2974
+ pointerY: function(event) {
2975
+ return event.pageY || (event.clientY +
2976
+ (document.documentElement.scrollTop || document.body.scrollTop));
2977
+ },
2978
+
2979
+ stop: function(event) {
2980
+ if (event.preventDefault) {
2981
+ event.preventDefault();
2982
+ event.stopPropagation();
2983
+ } else {
2984
+ event.returnValue = false;
2985
+ event.cancelBubble = true;
2986
+ }
2987
+ },
2988
+
2989
+ // find the first node with the given tagName, starting from the
2990
+ // node the event was triggered on; traverses the DOM upwards
2991
+ findElement: function(event, tagName) {
2992
+ var element = Event.element(event);
2993
+ while (element.parentNode && (!element.tagName ||
2994
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
2995
+ element = element.parentNode;
2996
+ return element;
2997
+ },
2998
+
2999
+ observers: false,
3000
+
3001
+ _observeAndCache: function(element, name, observer, useCapture) {
3002
+ if (!this.observers) this.observers = [];
3003
+ if (element.addEventListener) {
3004
+ this.observers.push([element, name, observer, useCapture]);
3005
+ element.addEventListener(name, observer, useCapture);
3006
+ } else if (element.attachEvent) {
3007
+ this.observers.push([element, name, observer, useCapture]);
3008
+ element.attachEvent('on' + name, observer);
3009
+ }
3010
+ },
3011
+
3012
+ unloadCache: function() {
3013
+ if (!Event.observers) return;
3014
+ for (var i = 0, length = Event.observers.length; i < length; i++) {
3015
+ Event.stopObserving.apply(this, Event.observers[i]);
3016
+ Event.observers[i][0] = null;
3017
+ }
3018
+ Event.observers = false;
3019
+ },
3020
+
3021
+ observe: function(element, name, observer, useCapture) {
3022
+ element = $(element);
3023
+ useCapture = useCapture || false;
3024
+
3025
+ if (name == 'keypress' &&
3026
+ (Prototype.Browser.WebKit || element.attachEvent))
3027
+ name = 'keydown';
3028
+
3029
+ Event._observeAndCache(element, name, observer, useCapture);
3030
+ },
3031
+
3032
+ stopObserving: function(element, name, observer, useCapture) {
3033
+ element = $(element);
3034
+ useCapture = useCapture || false;
3035
+
3036
+ if (name == 'keypress' &&
3037
+ (Prototype.Browser.WebKit || element.attachEvent))
3038
+ name = 'keydown';
3039
+
3040
+ if (element.removeEventListener) {
3041
+ element.removeEventListener(name, observer, useCapture);
3042
+ } else if (element.detachEvent) {
3043
+ try {
3044
+ element.detachEvent('on' + name, observer);
3045
+ } catch (e) {}
3046
+ }
3047
+ }
3048
+ });
3049
+
3050
+ /* prevent memory leaks in IE */
3051
+ if (Prototype.Browser.IE)
3052
+ Event.observe(window, 'unload', Event.unloadCache, false);
3053
+ var Position = {
3054
+ // set to true if needed, warning: firefox performance problems
3055
+ // NOT neeeded for page scrolling, only if draggable contained in
3056
+ // scrollable elements
3057
+ includeScrollOffsets: false,
3058
+
3059
+ // must be called before calling withinIncludingScrolloffset, every time the
3060
+ // page is scrolled
3061
+ prepare: function() {
3062
+ this.deltaX = window.pageXOffset
3063
+ || document.documentElement.scrollLeft
3064
+ || document.body.scrollLeft
3065
+ || 0;
3066
+ this.deltaY = window.pageYOffset
3067
+ || document.documentElement.scrollTop
3068
+ || document.body.scrollTop
3069
+ || 0;
3070
+ },
3071
+
3072
+ realOffset: function(element) {
3073
+ var valueT = 0, valueL = 0;
3074
+ do {
3075
+ valueT += element.scrollTop || 0;
3076
+ valueL += element.scrollLeft || 0;
3077
+ element = element.parentNode;
3078
+ } while (element);
3079
+ return [valueL, valueT];
3080
+ },
3081
+
3082
+ cumulativeOffset: function(element) {
3083
+ var valueT = 0, valueL = 0;
3084
+ do {
3085
+ valueT += element.offsetTop || 0;
3086
+ valueL += element.offsetLeft || 0;
3087
+ element = element.offsetParent;
3088
+ } while (element);
3089
+ return [valueL, valueT];
3090
+ },
3091
+
3092
+ positionedOffset: function(element) {
3093
+ var valueT = 0, valueL = 0;
3094
+ do {
3095
+ valueT += element.offsetTop || 0;
3096
+ valueL += element.offsetLeft || 0;
3097
+ element = element.offsetParent;
3098
+ if (element) {
3099
+ if(element.tagName=='BODY') break;
3100
+ var p = Element.getStyle(element, 'position');
3101
+ if (p == 'relative' || p == 'absolute') break;
3102
+ }
3103
+ } while (element);
3104
+ return [valueL, valueT];
3105
+ },
3106
+
3107
+ offsetParent: function(element) {
3108
+ if (element.offsetParent) return element.offsetParent;
3109
+ if (element == document.body) return element;
3110
+
3111
+ while ((element = element.parentNode) && element != document.body)
3112
+ if (Element.getStyle(element, 'position') != 'static')
3113
+ return element;
3114
+
3115
+ return document.body;
3116
+ },
3117
+
3118
+ // caches x/y coordinate pair to use with overlap
3119
+ within: function(element, x, y) {
3120
+ if (this.includeScrollOffsets)
3121
+ return this.withinIncludingScrolloffsets(element, x, y);
3122
+ this.xcomp = x;
3123
+ this.ycomp = y;
3124
+ this.offset = this.cumulativeOffset(element);
3125
+
3126
+ return (y >= this.offset[1] &&
3127
+ y < this.offset[1] + element.offsetHeight &&
3128
+ x >= this.offset[0] &&
3129
+ x < this.offset[0] + element.offsetWidth);
3130
+ },
3131
+
3132
+ withinIncludingScrolloffsets: function(element, x, y) {
3133
+ var offsetcache = this.realOffset(element);
3134
+
3135
+ this.xcomp = x + offsetcache[0] - this.deltaX;
3136
+ this.ycomp = y + offsetcache[1] - this.deltaY;
3137
+ this.offset = this.cumulativeOffset(element);
3138
+
3139
+ return (this.ycomp >= this.offset[1] &&
3140
+ this.ycomp < this.offset[1] + element.offsetHeight &&
3141
+ this.xcomp >= this.offset[0] &&
3142
+ this.xcomp < this.offset[0] + element.offsetWidth);
3143
+ },
3144
+
3145
+ // within must be called directly before
3146
+ overlap: function(mode, element) {
3147
+ if (!mode) return 0;
3148
+ if (mode == 'vertical')
3149
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
3150
+ element.offsetHeight;
3151
+ if (mode == 'horizontal')
3152
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
3153
+ element.offsetWidth;
3154
+ },
3155
+
3156
+ page: function(forElement) {
3157
+ var valueT = 0, valueL = 0;
3158
+
3159
+ var element = forElement;
3160
+ do {
3161
+ valueT += element.offsetTop || 0;
3162
+ valueL += element.offsetLeft || 0;
3163
+
3164
+ // Safari fix
3165
+ if (element.offsetParent == document.body)
3166
+ if (Element.getStyle(element,'position')=='absolute') break;
3167
+
3168
+ } while (element = element.offsetParent);
3169
+
3170
+ element = forElement;
3171
+ do {
3172
+ if (!window.opera || element.tagName=='BODY') {
3173
+ valueT -= element.scrollTop || 0;
3174
+ valueL -= element.scrollLeft || 0;
3175
+ }
3176
+ } while (element = element.parentNode);
3177
+
3178
+ return [valueL, valueT];
3179
+ },
3180
+
3181
+ clone: function(source, target) {
3182
+ var options = Object.extend({
3183
+ setLeft: true,
3184
+ setTop: true,
3185
+ setWidth: true,
3186
+ setHeight: true,
3187
+ offsetTop: 0,
3188
+ offsetLeft: 0
3189
+ }, arguments[2] || {})
3190
+
3191
+ // find page position of source
3192
+ source = $(source);
3193
+ var p = Position.page(source);
3194
+
3195
+ // find coordinate system to use
3196
+ target = $(target);
3197
+ var delta = [0, 0];
3198
+ var parent = null;
3199
+ // delta [0,0] will do fine with position: fixed elements,
3200
+ // position:absolute needs offsetParent deltas
3201
+ if (Element.getStyle(target,'position') == 'absolute') {
3202
+ parent = Position.offsetParent(target);
3203
+ delta = Position.page(parent);
3204
+ }
3205
+
3206
+ // correct by body offsets (fixes Safari)
3207
+ if (parent == document.body) {
3208
+ delta[0] -= document.body.offsetLeft;
3209
+ delta[1] -= document.body.offsetTop;
3210
+ }
3211
+
3212
+ // set position
3213
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
3214
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
3215
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
3216
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
3217
+ },
3218
+
3219
+ absolutize: function(element) {
3220
+ element = $(element);
3221
+ if (element.style.position == 'absolute') return;
3222
+ Position.prepare();
3223
+
3224
+ var offsets = Position.positionedOffset(element);
3225
+ var top = offsets[1];
3226
+ var left = offsets[0];
3227
+ var width = element.clientWidth;
3228
+ var height = element.clientHeight;
3229
+
3230
+ element._originalLeft = left - parseFloat(element.style.left || 0);
3231
+ element._originalTop = top - parseFloat(element.style.top || 0);
3232
+ element._originalWidth = element.style.width;
3233
+ element._originalHeight = element.style.height;
3234
+
3235
+ element.style.position = 'absolute';
3236
+ element.style.top = top + 'px';
3237
+ element.style.left = left + 'px';
3238
+ element.style.width = width + 'px';
3239
+ element.style.height = height + 'px';
3240
+ },
3241
+
3242
+ relativize: function(element) {
3243
+ element = $(element);
3244
+ if (element.style.position == 'relative') return;
3245
+ Position.prepare();
3246
+
3247
+ element.style.position = 'relative';
3248
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
3249
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
3250
+
3251
+ element.style.top = top + 'px';
3252
+ element.style.left = left + 'px';
3253
+ element.style.height = element._originalHeight;
3254
+ element.style.width = element._originalWidth;
3255
+ }
3256
+ }
3257
+
3258
+ // Safari returns margins on body which is incorrect if the child is absolutely
3259
+ // positioned. For performance reasons, redefine Position.cumulativeOffset for
3260
+ // KHTML/WebKit only.
3261
+ if (Prototype.Browser.WebKit) {
3262
+ Position.cumulativeOffset = function(element) {
3263
+ var valueT = 0, valueL = 0;
3264
+ do {
3265
+ valueT += element.offsetTop || 0;
3266
+ valueL += element.offsetLeft || 0;
3267
+ if (element.offsetParent == document.body)
3268
+ if (Element.getStyle(element, 'position') == 'absolute') break;
3269
+
3270
+ element = element.offsetParent;
3271
+ } while (element);
3272
+
3273
+ return [valueL, valueT];
3274
+ }
3275
+ }
3276
+
3277
+ Element.addMethods();
downloader/lib/Mage/Archive.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive
35
+ {
36
+
37
+ /**
38
+ * Archiver is used for compress.
39
+ */
40
+ const DEFAULT_ARCHIVER = 'gz';
41
+
42
+ /**
43
+ * Default packer for directory.
44
+ */
45
+ const TAPE_ARCHIVER = 'tar';
46
+
47
+ /**
48
+ * Current archiver is used for compress.
49
+ *
50
+ * @var Mage_Archiver_Tar|Mage_Archiver_Gz|Mage_Archiver_Bz
51
+ */
52
+ protected $_archiver=null;
53
+
54
+ /**
55
+ * Accessible formats for compress.
56
+ *
57
+ * @var array
58
+ */
59
+ protected $_formats = array(
60
+ 'tar' => 'tar',
61
+ 'gz' => 'gz',
62
+ 'gzip' => 'gz',
63
+ 'tgz' => 'tar.gz',
64
+ 'tgzip' => 'tar.gz',
65
+ 'bz' => 'bz',
66
+ 'bzip' => 'bz',
67
+ 'bzip2' => 'bz',
68
+ 'bz2' => 'bz',
69
+ 'tbz' => 'tar.bz',
70
+ 'tbzip' => 'tar.bz',
71
+ 'tbz2' => 'tar.bz',
72
+ 'tbzip2' => 'tar.bz');
73
+
74
+ /**
75
+ * Create object of current archiver by $extension.
76
+ *
77
+ * @param string $extension
78
+ * @return Mage_Archiver_Tar|Mage_Archiver_Gz|Mage_Archiver_Bz
79
+ */
80
+ protected function _getArchiver($extension)
81
+ {
82
+ if(array_key_exists(strtolower($extension), $this->_formats)) {
83
+ $format = $this->_formats[$extension];
84
+ } else {
85
+ $format = self::DEFAULT_ARCHIVER;
86
+ }
87
+ $class = 'Mage_Archive_'.ucfirst($format);
88
+ $this->_archiver = new $class();
89
+ return $this->_archiver;
90
+ }
91
+
92
+ /**
93
+ * Split current format to list of archivers.
94
+ *
95
+ * @param string $source
96
+ * @return array
97
+ */
98
+ protected function _getArchivers($source)
99
+ {
100
+ $ext = pathinfo($source, PATHINFO_EXTENSION);
101
+ if(!isset($this->_formats[$ext])) {
102
+ return array();
103
+ }
104
+ $format = $this->_formats[$ext];
105
+ if ($format) {
106
+ $archivers = explode('.', $format);
107
+ return $archivers;
108
+ }
109
+ return array();
110
+ }
111
+
112
+ /**
113
+ * Pack file or directory to archivers are parsed from extension.
114
+ *
115
+ * @param string $source
116
+ * @param string $destination
117
+ * @param boolean $skipRoot skip first level parent
118
+ * @return string Path to file
119
+ */
120
+ public function pack($source, $destination='packed.tgz', $skipRoot=false)
121
+ {
122
+ $archivers = $this->_getArchivers($destination);
123
+ $interimSource = '';
124
+ for ($i=0; $i<count($archivers); $i++ ) {
125
+ if ($i == (count($archivers) - 1)) {
126
+ $packed = $destination;
127
+ } else {
128
+ $packed = dirname($destination) . DS . '~tmp-'. microtime(true) . $archivers[$i] . '.' . $archivers[$i];
129
+ }
130
+ $source = $this->_getArchiver($archivers[$i])->pack($source, $packed, $skipRoot);
131
+ if ($interimSource && $i < count($archivers)) {
132
+ unlink($interimSource);
133
+ }
134
+ $interimSource = $source;
135
+ }
136
+ return $source;
137
+ }
138
+
139
+ /**
140
+ * Unpack file from archivers are parsed from extension.
141
+ * If $tillTar == true unpack file from archivers till
142
+ * meet TAR archiver.
143
+ *
144
+ * @param string $source
145
+ * @param string $destination
146
+ * @param boolean $tillTar
147
+ * @return string Path to file
148
+ */
149
+ public function unpack($source, $destination='.', $tillTar=false, $clearInterm = true)
150
+ {
151
+ $archivers = $this->_getArchivers($source);
152
+ $interimSource = '';
153
+ for ($i=count($archivers)-1; $i>=0; $i--) {
154
+ if ($tillTar && $archivers[$i] == self::TAPE_ARCHIVER) {
155
+ break;
156
+ }
157
+ if ($i == 0) {
158
+ $packed = rtrim($destination, DS) . DS;
159
+ } else {
160
+ $packed = rtrim($destination, DS) . DS . '~tmp-'. microtime(true) . $archivers[$i-1] . '.' . $archivers[$i-1];
161
+ }
162
+ $source = $this->_getArchiver($archivers[$i])->unpack($source, $packed);
163
+
164
+ //var_dump($packed, $source);
165
+
166
+ if ($clearInterm && $interimSource && $i >= 0) {
167
+ unlink($interimSource);
168
+ }
169
+ $interimSource = $source;
170
+ }
171
+ return $source;
172
+ }
173
+
174
+ /**
175
+ * Extract one file from TAR (Tape Archiver).
176
+ *
177
+ * @param string $file
178
+ * @param string $source
179
+ * @param string $destination
180
+ * @return string Path to file
181
+ */
182
+ public function extract($file, $source, $destination='.')
183
+ {
184
+ $tarFile = $this->unpack($source, $destination, true);
185
+ $resFile = $this->_getArchiver(self::TAPE_ARCHIVER)->extract($file, $tarFile, $destination);
186
+ if (!$this->isTar($source)) {
187
+ unlink($tarFile);
188
+ }
189
+ return $resFile;
190
+ }
191
+
192
+ /**
193
+ * Check file is archive.
194
+ *
195
+ * @param string $file
196
+ * @return boolean
197
+ */
198
+ public function isArchive($file)
199
+ {
200
+ $archivers = $this->_getArchivers($file);
201
+ if (count($archivers)) {
202
+ return true;
203
+ }
204
+ return false;
205
+ }
206
+
207
+ /**
208
+ * Check file is TAR.
209
+ *
210
+ * @param mixed $file
211
+ * @return boolean
212
+ */
213
+ public function isTar($file)
214
+ {
215
+ $archivers = $this->_getArchivers($file);
216
+ if (count($archivers)==1 && $archivers[0] == self::TAPE_ARCHIVER) {
217
+ return true;
218
+ }
219
+ return false;
220
+ }
221
+
222
+ }
downloader/lib/Mage/Archive/Abstract.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive_Abstract
35
+ {
36
+ /**
37
+ * Write data to file. If file can't be opened,
38
+ *
39
+ * @param string $destination
40
+ * @param string $data
41
+ * @return boolean
42
+ */
43
+ protected function _writeFile($destination, $data)
44
+ {
45
+ if(false === file_put_contents($destination, $data)) {
46
+ throw new Mage_Exception("Can't write to file: " . $destination);
47
+ }
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * Read data from file. If file can't be opened, throw to exception.
53
+ *
54
+ * @param string $source
55
+ * @return string
56
+ */
57
+ protected function _readFile($source)
58
+ {
59
+ $data = '';
60
+ if (is_file($source) && is_readable($source)) {
61
+ $data = @file_get_contents($source);
62
+ if ($data === false) {
63
+ throw new Mage_Exception("Can't get contents from: " . $source);
64
+ }
65
+ }
66
+ return $data;
67
+ }
68
+
69
+ /**
70
+ * Get file name from source (URI) without last extension.
71
+ *
72
+ * @param string $source
73
+ * @return string
74
+ */
75
+ public function getFilename($source, $withExtension=false)
76
+ {
77
+ $file = str_replace(dirname($source) . DS, '', $source);
78
+ if (!$withExtension) {
79
+ $file = substr($file, 0, strrpos($file, '.'));
80
+ }
81
+ return $file;
82
+ }
83
+
84
+ }
downloader/lib/Mage/Archive/Bz.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with bzip2 archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive_Bz extends Mage_Archive_Abstract implements Mage_Archive_Interface
35
+ {
36
+
37
+ /**
38
+ * Pack file by BZIP2 compressor.
39
+ *
40
+ * @param string $source
41
+ * @param string $destination
42
+ * @return string
43
+ */
44
+ public function pack($source, $destination)
45
+ {
46
+ $data = $this->_readFile($source);
47
+ $bzData = bzcompress($data, 9);
48
+ $this->_writeFile($destination, $bzData);
49
+ return $destination;
50
+ }
51
+
52
+ /**
53
+ * Unpack file by BZIP2 compressor.
54
+ *
55
+ * @param string $source
56
+ * @param string $destination
57
+ * @return string
58
+ */
59
+ public function unpack($source, $destination)
60
+ {
61
+ $data = '';
62
+ $bzPointer = bzopen($source, 'r' );
63
+ if (empty($bzPointer)) {
64
+ throw new Exception('Can\'t open BZ archive : ' . $source);
65
+ }
66
+ while (!feof($bzPointer)) {
67
+ $data .= bzread($bzPointer, 131072);
68
+ }
69
+ bzclose($bzPointer);
70
+ if (is_dir($destination)) {
71
+ $file = $this->getFilename($source);
72
+ $destination = $destination . $file;
73
+ }
74
+ echo $destination;
75
+ $this->_writeFile($destination, $data);
76
+ return $destination;
77
+ }
78
+
79
+ }
downloader/lib/Mage/Archive/Gz.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with gz archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive_Gz extends Mage_Archive_Abstract implements Mage_Archive_Interface
35
+ {
36
+ /**
37
+ * Pack file by GZ compressor.
38
+ *
39
+ * @param string $source
40
+ * @param string $destination
41
+ * @return string
42
+ */
43
+ public function pack($source, $destination)
44
+ {
45
+ $data = $this->_readFile($source);
46
+ $gzData = gzencode($data, 9);
47
+ $this->_writeFile($destination, $gzData);
48
+ return $destination;
49
+ }
50
+
51
+ /**
52
+ * Unpack file by GZ compressor.
53
+ *
54
+ * @param string $source
55
+ * @param string $destination
56
+ * @return string
57
+ */
58
+ public function unpack($source, $destination)
59
+ {
60
+ $gzPointer = gzopen($source, 'r' );
61
+ if (empty($gzPointer)) {
62
+ throw new Mage_Exception('Can\'t open GZ archive : ' . $source);
63
+ }
64
+ $data = '';
65
+ while (!gzeof($gzPointer)) {
66
+ $data .= gzread($gzPointer, 131072);
67
+ }
68
+ gzclose($gzPointer);
69
+ if (is_dir($destination)) {
70
+ $file = $this->getFilename($source);
71
+ $destination = $destination . $file;
72
+ }
73
+ $this->_writeFile($destination, $data);
74
+ return $destination;
75
+ }
76
+
77
+ }
downloader/lib/Mage/Archive/Interface.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Interface for work with archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ interface Mage_Archive_Interface
35
+ {
36
+ /**
37
+ * Pack file or directory.
38
+ *
39
+ * @param string $source
40
+ * @param string $destination
41
+ * @return string
42
+ */
43
+ public function pack($source, $destination);
44
+
45
+ /**
46
+ * Unpack file or directory.
47
+ *
48
+ * @param string $source
49
+ * @param string $destination
50
+ * @return string
51
+ */
52
+ public function unpack($source, $destination);
53
+ }
downloader/lib/Mage/Archive/Tar.php ADDED
@@ -0,0 +1,372 @@