| 1 |
Index: includes/common.inc
|
| 2 |
===================================================================
|
| 3 |
RCS file: /cvs/drupal/drupal/includes/common.inc,v
|
| 4 |
retrieving revision 1.475
|
| 5 |
diff -u -r1.475 common.inc
|
| 6 |
--- includes/common.inc 28 Aug 2005 16:19:24 -0000 1.475
|
| 7 |
+++ includes/common.inc 31 Aug 2005 15:17:23 -0000
|
| 8 |
@@ -1461,6 +1461,10 @@
|
| 9 |
* The internal name used to refer to the field.
|
| 10 |
* @param $value
|
| 11 |
* The stored data.
|
| 12 |
+ * @param $edit
|
| 13 |
+ * The array name to prefix to the $name.
|
| 14 |
+ * @param $attributes
|
| 15 |
+ * An array of HTML attributes for the input tag.
|
| 16 |
* @return
|
| 17 |
* A themed HTML string representing the hidden field.
|
| 18 |
*
|
| 19 |
@@ -1468,8 +1472,8 @@
|
| 20 |
* but be sure to validate the data on the receiving page as it is possible for
|
| 21 |
* an attacker to change the value before it is submitted.
|
| 22 |
*/
|
| 23 |
-function form_hidden($name, $value, $edit = 'edit') {
|
| 24 |
- return '<input type="hidden" name="'. $edit .'['. $name .']" value="'. check_plain($value) ."\" />\n";
|
| 25 |
+function form_hidden($name, $value, $edit = 'edit', $attributes = NULL) {
|
| 26 |
+ return '<input type="hidden" name="'. $edit .'['. $name .']" id="'. form_clean_id($edit .'-'. $name) .'" value="'. check_plain($value) .'"'. drupal_attributes($attributes) ." />\n";
|
| 27 |
}
|
| 28 |
|
| 29 |
/**
|
| 30 |
@@ -1488,7 +1492,7 @@
|
| 31 |
* A themed HTML string representing the button.
|
| 32 |
*/
|
| 33 |
function form_button($value, $name = 'op', $type = 'submit', $attributes = NULL) {
|
| 34 |
- return '<input type="'. $type .'" class="form-'. $type .'" name="'. $name .'" value="'. check_plain($value) .'" '. drupal_attributes($attributes) ." />\n";
|
| 35 |
+ return '<input type="'. $type .'" class="form-'. $type .'" name="'. $name .'" id="'. form_clean_id($name) .'" value="'. check_plain($value) .'" '. drupal_attributes($attributes) ." />\n";
|
| 36 |
}
|
| 37 |
|
| 38 |
/**
|
| 39 |
@@ -1806,6 +1810,51 @@
|
| 40 |
}
|
| 41 |
|
| 42 |
/**
|
| 43 |
+ * Generates a Javascript call, while importing the arguments as is.
|
| 44 |
+ * PHP arrays are turned into JS objects to preserve keys. This means the array
|
| 45 |
+ * keys must conform to JS's member naming rules.
|
| 46 |
+ *
|
| 47 |
+ * @param $function
|
| 48 |
+ * The name of the function to call.
|
| 49 |
+ * @param $arguments
|
| 50 |
+ * An array of arguments.
|
| 51 |
+ */
|
| 52 |
+function drupal_call_js($function) {
|
| 53 |
+ $arguments = func_get_args();
|
| 54 |
+ array_shift($arguments);
|
| 55 |
+ $args = array();
|
| 56 |
+ foreach ($arguments as $arg) {
|
| 57 |
+ $args[] = drupal_to_js($arg);
|
| 58 |
+ }
|
| 59 |
+ $output = '<script type="text/javascript">'. $function .'('. implode(', ', $args) .');</script>';
|
| 60 |
+ return $output;
|
| 61 |
+}
|
| 62 |
+
|
| 63 |
+/**
|
| 64 |
+ * Converts a PHP variable into its Javascript equivalent.
|
| 65 |
+ */
|
| 66 |
+function drupal_to_js($var) {
|
| 67 |
+ switch (gettype($var)) {
|
| 68 |
+ case 'boolean':
|
| 69 |
+ case 'integer':
|
| 70 |
+ case 'double':
|
| 71 |
+ return $var;
|
| 72 |
+ case 'resource':
|
| 73 |
+ case 'string':
|
| 74 |
+ return '"'. str_replace(array("\r", "\n"), array('\r', '\n'), addslashes($var)) .'"';
|
| 75 |
+ case 'array':
|
| 76 |
+ case 'object':
|
| 77 |
+ $output = array();
|
| 78 |
+ foreach ($var as $k => $v) {
|
| 79 |
+ $output[] = $k .': '. drupal_to_js($v);
|
| 80 |
+ }
|
| 81 |
+ return '{ '. implode(', ', $output) .' }';
|
| 82 |
+ default:
|
| 83 |
+ return 'null';
|
| 84 |
+ }
|
| 85 |
+}
|
| 86 |
+
|
| 87 |
+/**
|
| 88 |
* Implode a PHP array into a string that can be decoded by the autocomplete JS routines.
|
| 89 |
*
|
| 90 |
* Items are separated by double pipes. Each item consists of a key-value pair
|
| 91 |
cvs diff: Diffing misc
|
| 92 |
Index: misc/autocomplete.js
|
| 93 |
===================================================================
|
| 94 |
RCS file: /cvs/drupal/drupal/misc/autocomplete.js,v
|
| 95 |
retrieving revision 1.4
|
| 96 |
diff -u -r1.4 autocomplete.js
|
| 97 |
--- misc/autocomplete.js 11 Aug 2005 13:00:17 -0000 1.4
|
| 98 |
+++ misc/autocomplete.js 31 Aug 2005 15:17:24 -0000
|
| 99 |
@@ -17,10 +17,9 @@
|
| 100 |
if (!acdb[uri]) {
|
| 101 |
acdb[uri] = new ACDB(uri);
|
| 102 |
}
|
| 103 |
- id = input.id.substr(0, input.id.length - 13);
|
| 104 |
- input = document.getElementById(id);
|
| 105 |
+ input = $(input.id.substr(0, input.id.length - 13));
|
| 106 |
input.setAttribute('autocomplete', 'OFF');
|
| 107 |
- input.form.onsubmit = autocompleteSubmit;
|
| 108 |
+ addSubmitEvent(input.form, autocompleteSubmit);
|
| 109 |
new jsAC(input, acdb[uri]);
|
| 110 |
}
|
| 111 |
}
|
| 112 |
Index: misc/drupal.css
|
| 113 |
===================================================================
|
| 114 |
RCS file: /cvs/drupal/drupal/misc/drupal.css,v
|
| 115 |
retrieving revision 1.114
|
| 116 |
diff -u -r1.114 drupal.css
|
| 117 |
--- misc/drupal.css 22 Aug 2005 20:29:00 -0000 1.114
|
| 118 |
+++ misc/drupal.css 31 Aug 2005 15:17:24 -0000
|
| 119 |
@@ -579,6 +579,28 @@
|
| 120 |
}
|
| 121 |
|
| 122 |
/*
|
| 123 |
+** Progressbar styles
|
| 124 |
+*/
|
| 125 |
+.progress {
|
| 126 |
+ font-weight: bold;
|
| 127 |
+}
|
| 128 |
+.progress .bar {
|
| 129 |
+ background: #fff url('progress.gif');
|
| 130 |
+ border: 1px solid #00375a;
|
| 131 |
+ height: 1.5em;
|
| 132 |
+ margin-top: 0.2em;
|
| 133 |
+}
|
| 134 |
+.progress .filled {
|
| 135 |
+ background: #0072b9;
|
| 136 |
+ height: 1.33em;
|
| 137 |
+ border-bottom: 0.67em solid #004a73;
|
| 138 |
+ width: 0%;
|
| 139 |
+}
|
| 140 |
+.progress .percentage {
|
| 141 |
+ float: right;
|
| 142 |
+}
|
| 143 |
+
|
| 144 |
+/*
|
| 145 |
** Collapsing fieldsets
|
| 146 |
*/
|
| 147 |
html.js fieldset.collapsed {
|
| 148 |
Index: misc/drupal.js
|
| 149 |
===================================================================
|
| 150 |
RCS file: /cvs/drupal/drupal/misc/drupal.js,v
|
| 151 |
retrieving revision 1.6
|
| 152 |
diff -u -r1.6 drupal.js
|
| 153 |
--- misc/drupal.js 18 Aug 2005 05:14:06 -0000 1.6
|
| 154 |
+++ misc/drupal.js 31 Aug 2005 15:17:25 -0000
|
| 155 |
@@ -102,6 +102,42 @@
|
| 156 |
}
|
| 157 |
|
| 158 |
/**
|
| 159 |
+ * Redirects a button's form submission to a hidden iframe and displays the result
|
| 160 |
+ * in a given wrapper. The iframe should contain a call to
|
| 161 |
+ * window.parent.iframeHandler() after submission.
|
| 162 |
+ */
|
| 163 |
+function redirectFormButton(uri, button, handler) {
|
| 164 |
+ // Insert the iframe
|
| 165 |
+ var div = document.createElement('div');
|
| 166 |
+ div.innerHTML = '<iframe name="redirect-target" id="redirect-target" src="" style="width:0px;height:0px;border:0;"></iframe>';
|
| 167 |
+ button.parentNode.appendChild(div);
|
| 168 |
+
|
| 169 |
+ // Trap the button
|
| 170 |
+ button.onfocus = function() {
|
| 171 |
+ button.onclick = function() {
|
| 172 |
+ // Prepare vars for use in anonymous function.
|
| 173 |
+ var button = this;
|
| 174 |
+ var action = button.form.action;
|
| 175 |
+ var target = button.form.target;
|
| 176 |
+ // Redirect form submission
|
| 177 |
+ this.form.action = uri;
|
| 178 |
+ this.form.target = 'redirect-target';
|
| 179 |
+ handler.onsubmit();
|
| 180 |
+ // Set iframe handler for later
|
| 181 |
+ window.iframeHandler = function (data) {
|
| 182 |
+ // Restore form submission
|
| 183 |
+ button.form.action = action;
|
| 184 |
+ button.form.target = target;
|
| 185 |
+ handler.oncomplete(data);
|
| 186 |
+ }
|
| 187 |
+ }
|
| 188 |
+ }
|
| 189 |
+ button.onblur = function() {
|
| 190 |
+ button.onclick = null;
|
| 191 |
+ }
|
| 192 |
+}
|
| 193 |
+
|
| 194 |
+/**
|
| 195 |
* Adds a function to the window onload event
|
| 196 |
*/
|
| 197 |
function addLoadEvent(func) {
|
| 198 |
@@ -118,6 +154,21 @@
|
| 199 |
}
|
| 200 |
|
| 201 |
/**
|
| 202 |
+ * Adds a function to the window onload event
|
| 203 |
+ */
|
| 204 |
+function addSubmitEvent(form, func) {
|
| 205 |
+ var oldSubmit = form.onsubmit;
|
| 206 |
+ if (typeof oldSubmit != 'function') {
|
| 207 |
+ form.onsubmit = func;
|
| 208 |
+ }
|
| 209 |
+ else {
|
| 210 |
+ form.onsubmit = function() {
|
| 211 |
+ return oldSubmit() && func();
|
| 212 |
+ }
|
| 213 |
+ }
|
| 214 |
+}
|
| 215 |
+
|
| 216 |
+/**
|
| 217 |
* Retrieves the absolute position of an element on the screen
|
| 218 |
*/
|
| 219 |
function absolutePosition(el) {
|
| 220 |
@@ -196,7 +247,7 @@
|
| 221 |
*/
|
| 222 |
function removeNode(node) {
|
| 223 |
if (typeof node == 'string') {
|
| 224 |
- node = document.getElementById(node);
|
| 225 |
+ node = $(node);
|
| 226 |
}
|
| 227 |
if (node && node.parentNode) {
|
| 228 |
return node.parentNode.removeChild(node);
|
| 229 |
@@ -205,3 +256,10 @@
|
| 230 |
return false;
|
| 231 |
}
|
| 232 |
}
|
| 233 |
+
|
| 234 |
+/**
|
| 235 |
+ * Wrapper around document.getElementById().
|
| 236 |
+ */
|
| 237 |
+function $(id) {
|
| 238 |
+ return document.getElementById(id);
|
| 239 |
+}
|
| 240 |
Index: misc/progress.gif
|
| 241 |
===================================================================
|
| 242 |
RCS file: misc/progress.gif
|
| 243 |
diff -N misc/progress.gif
|
| 244 |
Binary files /dev/null and progress.gif differ
|
| 245 |
Index: misc/progress.js
|
| 246 |
===================================================================
|
| 247 |
RCS file: misc/progress.js
|
| 248 |
diff -N misc/progress.js
|
| 249 |
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
| 250 |
+++ misc/progress.js 31 Aug 2005 15:17:25 -0000
|
| 251 |
@@ -0,0 +1,80 @@
|
| 252 |
+/**
|
| 253 |
+ * A progressbar object. Initialized with the given id. Must be inserted into
|
| 254 |
+ * the DOM afterwards through progressBar.element.
|
| 255 |
+ *
|
| 256 |
+ * e.g. pb = new progressBar('myProgressBar');
|
| 257 |
+ * some_element.appendChild(pb.element);
|
| 258 |
+ */
|
| 259 |
+function progressBar(id) {
|
| 260 |
+ var pb = this;
|
| 261 |
+ this.id = id;
|
| 262 |
+
|
| 263 |
+ this.element = document.createElement('div');
|
| 264 |
+ this.element.id = id;
|
| 265 |
+ this.element.className = 'progress';
|
| 266 |
+ this.element.innerHTML = '<div class="percentage"></div>'+
|
| 267 |
+ '<div class="status"> </div>'+
|
| 268 |
+ '<div class="bar"><div class="filled"></div></div>';
|
| 269 |
+}
|
| 270 |
+
|
| 271 |
+/**
|
| 272 |
+ * Set the percentage and status message for the progressbar.
|
| 273 |
+ */
|
| 274 |
+progressBar.prototype.setProgress = function (percentage, status) {
|
| 275 |
+ var divs = this.element.getElementsByTagName('div');
|
| 276 |
+ for (i in divs) {
|
| 277 |
+ if (percentage >= 0) {
|
| 278 |
+ if (hasClass(divs[i], 'filled')) {
|
| 279 |
+ divs[i].style.width = percentage + '%';
|
| 280 |
+ }
|
| 281 |
+ if (hasClass(divs[i], 'percentage')) {
|
| 282 |
+ divs[i].innerHTML = percentage + '%';
|
| 283 |
+ }
|
| 284 |
+ }
|
| 285 |
+ if (hasClass(divs[i], 'status')) {
|
| 286 |
+ divs[i].innerHTML = status;
|
| 287 |
+ }
|
| 288 |
+ }
|
| 289 |
+}
|
| 290 |
+
|
| 291 |
+/**
|
| 292 |
+ * Start monitoring progress via Ajax.
|
| 293 |
+ */
|
| 294 |
+progressBar.prototype.startMonitoring = function (uri, delay) {
|
| 295 |
+ this.delay = delay;
|
| 296 |
+ this.uri = uri;
|
| 297 |
+ this.sendPing();
|
| 298 |
+}
|
| 299 |
+
|
| 300 |
+/**
|
| 301 |
+ * Stop monitoring progress via Ajax.
|
| 302 |
+ */
|
| 303 |
+progressBar.prototype.stopMonitoring = function () {
|
| 304 |
+ clearTimeout(this.timer);
|
| 305 |
+}
|
| 306 |
+
|
| 307 |
+/**
|
| 308 |
+ * Request progress data from server.
|
| 309 |
+ */
|
| 310 |
+progressBar.prototype.sendPing = function () {
|
| 311 |
+ if (this.timer) {
|
| 312 |
+ clearTimeout(this.timer);
|
| 313 |
+ }
|
| 314 |
+ HTTPGet(this.uri, this.receivePing, this);
|
| 315 |
+}
|
| 316 |
+
|
| 317 |
+/**
|
| 318 |
+ * HTTP callback function. Passes data back to the progressbar and sets a new
|
| 319 |
+ * timer for the next ping.
|
| 320 |
+ */
|
| 321 |
+progressBar.prototype.receivePing = function(string, xmlhttp, pb) {
|
| 322 |
+ if (xmlhttp.status != 200) {
|
| 323 |
+ return alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ pb.uri);
|
| 324 |
+ }
|
| 325 |
+ // Split into values
|
| 326 |
+ var matches = string.length > 0 ? string.split('|') : [];
|
| 327 |
+ if (matches.length >= 2) {
|
| 328 |
+ pb.setProgress(matches[0], matches[1]);
|
| 329 |
+ }
|
| 330 |
+ pb.timer = setTimeout(function() { pb.sendPing(); }, pb.delay);
|
| 331 |
+}
|
| 332 |
Index: misc/upload.js
|
| 333 |
===================================================================
|
| 334 |
RCS file: misc/upload.js
|
| 335 |
diff -N misc/upload.js
|
| 336 |
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
| 337 |
+++ misc/upload.js 31 Aug 2005 15:17:25 -0000
|
| 338 |
@@ -0,0 +1,59 @@
|
| 339 |
+// Global Killswitch
|
| 340 |
+if (isJsEnabled()) {
|
| 341 |
+ addLoadEvent(uploadAutoAttach);
|
| 342 |
+}
|
| 343 |
+
|
| 344 |
+/**
|
| 345 |
+ * Attaches the upload behaviour to the upload form.
|
| 346 |
+ */
|
| 347 |
+function uploadAutoAttach() {
|
| 348 |
+ var acdb = [];
|
| 349 |
+ var inputs = document.getElementsByTagName('input');
|
| 350 |
+ for (i = 0; input = inputs[i]; i++) {
|
| 351 |
+ if (input && hasClass(input, 'upload')) {
|
| 352 |
+ var uri = input.value;
|
| 353 |
+ var button = input.id.substr(5);
|
| 354 |
+ var wrapper = button + '-wrapper';
|
| 355 |
+ var hide = button + '-hide';
|
| 356 |
+ var upload = new jsUpload(uri, button, wrapper, hide);
|
| 357 |
+ }
|
| 358 |
+ }
|
| 359 |
+}
|
| 360 |
+
|
| 361 |
+/**
|
| 362 |
+ * JS Upload object
|
| 363 |
+ */
|
| 364 |
+function jsUpload(uri, button, wrapper, hide) {
|
| 365 |
+ var upload = this;
|
| 366 |
+ this.button = button;
|
| 367 |
+ this.wrapper = wrapper;
|
| 368 |
+ this.hide = hide;
|
| 369 |
+ redirectFormButton(uri, $(button), this);
|
| 370 |
+}
|
| 371 |
+
|
| 372 |
+/**
|
| 373 |
+ * Handler for the form redirection submission.
|
| 374 |
+ */
|
| 375 |
+jsUpload.prototype.onsubmit = function () {
|
| 376 |
+ var hide = $(this.hide);
|
| 377 |
+ // Insert progressbar and stretch to take the same space.
|
| 378 |
+ this.progress = new progressBar('uploadprogress');
|
| 379 |
+ this.progress.setProgress(-1, 'Uploading file...');
|
| 380 |
+ this.progress.element.style.width = '28em';
|
| 381 |
+ this.progress.element.style.height = hide.offsetHeight +'px';
|
| 382 |
+ hide.parentNode.insertBefore(this.progress.element, hide);
|
| 383 |
+ // Hide file form
|
| 384 |
+ hide.style.display = 'none';
|
| 385 |
+}
|
| 386 |
+
|
| 387 |
+/**
|
| 388 |
+ * Handler for the form redirection completion.
|
| 389 |
+ */
|
| 390 |
+jsUpload.prototype.oncomplete = function (data) {
|
| 391 |
+ // Remove progressbar
|
| 392 |
+ removeNode(this.progress);
|
| 393 |
+ this.progress = null;
|
| 394 |
+ // Replace form and re-attach behaviour
|
| 395 |
+ $(this.wrapper).innerHTML = data;
|
| 396 |
+ uploadAutoAttach();
|
| 397 |
+}
|
| 398 |
\ No newline at end of file
|
| 399 |
cvs diff: Diffing modules
|
| 400 |
Index: modules/upload.module
|
| 401 |
===================================================================
|
| 402 |
RCS file: /cvs/drupal/drupal/modules/upload.module,v
|
| 403 |
retrieving revision 1.49
|
| 404 |
diff -u -r1.49 upload.module
|
| 405 |
--- modules/upload.module 30 Aug 2005 15:22:29 -0000 1.49
|
| 406 |
+++ modules/upload.module 31 Aug 2005 15:17:26 -0000
|
| 407 |
@@ -60,6 +60,12 @@
|
| 408 |
'access' => user_access('administer site configuration'),
|
| 409 |
'type' => MENU_NORMAL_ITEM
|
| 410 |
);
|
| 411 |
+ $items[] = array(
|
| 412 |
+ 'path' => 'upload/js',
|
| 413 |
+ 'callback' => 'upload_js',
|
| 414 |
+ 'access' => user_access('upload files'),
|
| 415 |
+ 'type' => MENU_CALLBACK
|
| 416 |
+ );
|
| 417 |
}
|
| 418 |
else {
|
| 419 |
// Add handlers for previewing new uploads.
|
| 420 |
@@ -378,8 +384,18 @@
|
| 421 |
}
|
| 422 |
|
| 423 |
function upload_form($node) {
|
| 424 |
+ drupal_add_js('misc/progress.js');
|
| 425 |
+ drupal_add_js('misc/upload.js');
|
| 426 |
+
|
| 427 |
+ $output = '<div id="fileop-wrapper">'. _upload_form($node) .'</div>';
|
| 428 |
+
|
| 429 |
+ return '<div class="attachments">'. form_group_collapsible(t('File attachments'), $output, empty($node->files), t('Changes made to the attachments are not permanent until you save this post. The first "listed" file will be included in RSS feeds.')) .'</div>';
|
| 430 |
+}
|
| 431 |
+
|
| 432 |
+function _upload_form($node) {
|
| 433 |
$header = array(t('Delete'), t('List'), t('Url'), t('Size'));
|
| 434 |
$rows = array();
|
| 435 |
+ $output = '';
|
| 436 |
|
| 437 |
if (is_array($node->files)) {
|
| 438 |
foreach ($node->files as $key => $file) {
|
| 439 |
@@ -393,15 +409,19 @@
|
| 440 |
}
|
| 441 |
|
| 442 |
if (count($node->files)) {
|
| 443 |
- $output = theme('table', $header, $rows);
|
| 444 |
+ $output .= theme('table', $header, $rows);
|
| 445 |
}
|
| 446 |
|
| 447 |
if (user_access('upload files')) {
|
| 448 |
+ $output .= '<div id="fileop-hide">';
|
| 449 |
$output .= form_file(t('Attach new file'), "upload", 40);
|
| 450 |
$output .= form_button(t('Attach'), 'fileop');
|
| 451 |
+ // The class triggers the js upload behaviour.
|
| 452 |
+ $output .= form_hidden('fileop', url('upload/js', NULL, NULL, TRUE), 'edit', array('class' => 'upload'));
|
| 453 |
+ $output .= '</div>';
|
| 454 |
}
|
| 455 |
|
| 456 |
- return '<div class="attachments">'. form_group_collapsible(t('File attachments'), $output, empty($node->files), t('Changes made to the attachments are not permanent until you save this post. The first "listed" file will be included in RSS feeds.')) .'</div>';
|
| 457 |
+ return $output;
|
| 458 |
}
|
| 459 |
|
| 460 |
function upload_load($node) {
|
| 461 |
@@ -438,4 +458,16 @@
|
| 462 |
return $file;
|
| 463 |
}
|
| 464 |
|
| 465 |
-
|
| 466 |
+/**
|
| 467 |
+ * Menu-callback for JavaScript-based uploads.
|
| 468 |
+ */
|
| 469 |
+function upload_js() {
|
| 470 |
+ // We only do the upload.module part of the node validation process.
|
| 471 |
+ $node = array2object($_POST['edit']);
|
| 472 |
+ upload_nodeapi(&$node, 'validate', NULL);
|
| 473 |
+ $output = theme('status_messages') . _upload_form($node);
|
| 474 |
+
|
| 475 |
+ // We send the updated file attachments form.
|
| 476 |
+ print drupal_call_js('window.parent.iframeHandler', $output);
|
| 477 |
+ exit;
|
| 478 |
+}
|