/[drupal]/contributions/sandbox/unconed/soc/jsupload.patch
ViewVC logotype

Contents of /contributions/sandbox/unconed/soc/jsupload.patch

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Wed Aug 31 23:05:05 2005 UTC (4 years, 2 months ago) by unconed
Branch: MAIN
CVS Tags: HEAD
File MIME type: text/x-patch
- Putting SoC stuff up
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">&nbsp;</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 +}

  ViewVC Help
Powered by ViewVC 1.1.2