1 // requires prototype.js for now
3 // true to use the File API if available
4 // TODO: progress reporting
5 // TODO: start reporting
6 // TODO: utf8 filenames
7 // TODO: quotes in filenames(?)
8 var bse_use_file_api = true;
10 var BSEAPI = Class.create
12 initialize: function(parameters) {
13 if (!parameters) parameters = {};
14 this.initialized = true;
15 this.onException = function(e) {
18 this.onFailure = function(error) { alert(error.message); };
20 this.onConfig = parameters.onConfig;
23 _load_csrfp: function () {
27 name: this._csrfp_names,
28 onSuccess: function(csrfp) {
30 window.setTimeout(this._load_csrfp.bind(this), 600000);
32 onFailure: function() {
38 _load_config: function() {
41 onSuccess:function(conf) {
46 onFailure: function(err) {
50 // logon to the server
51 // logon - logon name of user
52 // password - password of user
53 // onSuccess - called on successful logon (no parameters)
54 // onFailure - called with an error object on failure.
55 logon: function(parameters) {
56 var success = parameters.onSuccess;
57 if (!success) this._badparm("logon() missing onSuccess parameter");
58 var failure = parameters.onFailure;
59 if (!failure) failure = this.onFailure;
60 if (parameters.logon == null)
61 this._badparm("logon() Missing logon parameter");
62 if (parameters.password == null)
63 this._badparm("logon() Missing password parameter");
64 new Ajax.Request('/cgi-bin/admin/logon.pl',
68 logon: parameters.logon,
69 password: parameters.password
71 onSuccess: function (success, failure, resp) {
72 if (resp.responseJSON) {
73 if(resp.responseJSON.success != 0) {
75 success(resp.responseJSON.user);
78 failure(this._wrap_json_failure(resp), resp);
82 failure(this._wrap_nojson_failure(resp), resp);
84 }.bind(this, success, failure),
85 onFailure: function (failure, resp) {
86 failure(this._wrap_req_failure(resp), resp);
87 }.bind(this, failure),
88 onException: this.onException
91 userinfo: function(parameters) {
92 var success = parameters.onSuccess;
93 if (!success) this._badparm("logon() missing onSuccess parameter");
94 var failure = parameters.onFailure;
95 if (!failure) failure = this.onFailure;
96 new Ajax.Request('/cgi-bin/admin/logon.pl',
101 onSuccess: function (success, failure, resp) {
102 if (resp.responseJSON) {
103 if(resp.responseJSON.success != 0) {
104 success(resp.responseJSON);
107 failure(this._wrap_json_failure(resp), resp);
111 failure(this._wrap_nojson_failure(resp), resp);
113 }.bind(this, success, failure),
114 onFailure: function (failure, resp) {
115 failure(this._wrap_req_failure(resp), resp);
116 }.bind(this, failure),
117 onException: this.onException
120 logoff: function(parameters) {
121 var success = parameters.onSuccess;
122 if (!success) this._badparm("logon() missing onSuccess parameter");
123 var failure = parameters.onFailure;
124 if (!failure) failure = this.onFailure;
125 new Ajax.Request('/cgi-bin/admin/logon.pl',
130 onSuccess: function (success, failure, resp) {
131 if (resp.responseJSON) {
132 if(resp.responseJSON.success != 0) {
136 failure(this._wrap_json_failure(resp), resp);
140 failure(this._wrap_nojson_failure(resp), resp);
142 }.bind(this, success, failure),
143 onFailure: function (failure, resp) {
144 failure(this._wrap_req_failure(resp), resp);
145 }.bind(this, failure),
146 onException: this.onException
149 // fetch a tree of articles;
150 // id - parent of tree to fetch
151 // depth - optional depth of tree to fetch (default is large)
152 // onSuccess - called with tree on success
153 // onFailure - called with error object on failure
154 tree: function(parameters) {
155 var success = parameters.onSuccess;
156 if (!success) this._badparm("tree() missing onSuccess parameter");
157 var failure = parameters.onFailure;
158 if (!failure) failure = this.onFailure;
159 var req_parms = { id: -1, a_tree: 1 };
161 req_parms.id = parameters.id;
162 if (parameters.depth)
163 req_parms.depth = parameters.depth;
164 new Ajax.Request('/cgi-bin/admin/add.pl',
166 parameters: req_parms,
167 onSuccess: function(success, failure, resp) {
168 if (resp.responseJSON) {
169 if (resp.responseJSON.success != 0) {
170 success(resp.responseJSON.articles);
173 failure(this._wrap_json_failure(resp), resp);
177 failure(this._wrap_nojson_failure(resp), resp);
179 }.bind(this, success, failure),
180 onFailure: function(failure, resp) {
181 failure(this._wrap_req_failure(resp), resp);
182 }.bind(this, failure),
183 onException: this.onException
186 article: function(parameters) {
187 var success = parameters.onSuccess;
188 if (!success) this._badparm("tree() missing onSuccess parameter");
189 var failure = parameters.onFailure;
190 if (!failure) failure = this.onFailure;
191 if (parameters.id == null)
192 this._badparm("article() missing id parameter");
193 var req_parms = { a_article: 1, id: parameters.id };
194 new Ajax.Request('/cgi-bin/admin/add.pl',
196 parameters: req_parms,
197 onSuccess: function(success, failure, resp) {
198 if (resp.responseJSON) {
199 if (resp.responseJSON.success != 0) {
200 success(resp.responseJSON.article);
203 failure(this._wrap_json_failure(resp), resp);
207 failure(this._wrap_nojson_failure(resp), resp);
209 }.bind(this, success, failure),
210 onFailure: function(failure, resp) {
211 failure(this._wrap_req_failure(resp), resp);
212 }.bind(this, failure),
213 onException: this.onException
216 // create a new article, accepts all article fields except id
217 new_article: function(parameters) {
218 var success = parameters.onSuccess;
219 if (!success) this._badparm("tree() missing onSuccess parameter");
220 var failure = parameters.onFailure;
221 if (!failure) failure = this.onFailure;
222 if (parameters.title == null)
223 this._badparm("new_article() missing title parameter");
224 if (parameters.parentid == null)
225 this._badparm("new_article() missing parentid parameter");
226 if (parameters.id != null)
227 this._badparm("new_article() can't accept an id parameter");
228 delete parameters.onSuccess;
229 delete parameters.onFailure;
230 this._do_add_request("save", parameters,
231 function(success, resp) {
232 success(resp.article);
233 }.bind(this, success),
236 save_article: function(parameters) {
237 var success = parameters.onSuccess;
238 if (!success) this._badparm("tree() missing onSuccess parameter");
239 var failure = parameters.onFailure;
240 if (!failure) failure = this.onFailure;
241 if (parameters.id == null)
242 this._badparm("save_article() missing id parameter");
243 if (parameters.lastModified == null)
244 this._badparm("save_article() missing lastModified parameter");
245 delete parameters.onSuccess;
246 delete parameters.onFailure;
247 this._do_add_request("save", parameters,
248 function(success, result) {
249 success(result.article);
250 }.bind(this, success),
253 get_base_config: function(parameters) {
254 var success = parameters.onSuccess;
255 if (!success) this._badparm("get_config() missing onSuccess parameter");
256 var failure = parameters.onFailure;
257 if (!failure) failure = this.onFailure;
258 delete parameters.onSuccess;
259 delete parameters.onFailure;
260 this._do_api_request("a_config", parameters, success, failure);
262 get_config: function(parameters) {
263 var success = parameters.onSuccess;
264 if (!success) this._badparm("get_config() missing onSuccess parameter");
265 var failure = parameters.onFailure;
266 if (!failure) failure = this.onFailure;
267 delete parameters.onSuccess;
268 delete parameters.onFailure;
269 if (parameters.id == null && parameters.parentid == null)
270 this._badparm("get_config() missing both id and parentid");
271 this._do_add_request("a_config", parameters, success, failure);
273 get_csrfp: function(parameters) {
274 var success = parameters.onSuccess;
275 if (!success) this._badparm("get_csrfp() missing onSuccess parameter");
276 var failure = parameters.onFailure;
277 if (!failure) failure = this.onFailure;
278 delete parameters.onSuccess;
279 delete parameters.onFailure;
280 if (parameters.id == null && parameters.id == null)
281 this._badparm("get_csrfp() missing both id and parentid");
282 this._do_add_request("a_csrfp", parameters,
283 function(success, result) {
284 success(result.tokens);
285 }.bind(this, success),
288 get_file_progress: function(parameters) {
289 var success = parameters.onSuccess;
290 if (!success) this._badparm("get_file_progress() missing onSuccess parameter");
291 var failure = parameters.onFailure;
292 if (!failure) failure = this.onFailure;
293 delete parameters.onSuccess;
294 delete parameters.onFailure;
295 if (parameters._upload == null)
296 this._badparm("get_file_progress() missing _upload");
297 this._do_request("/cgi-bin/fileprogress.pl", null, parameters,
298 function(success, result) {
299 success(result.progress);
300 }.bind(this, success),
303 thumb_link: function(im, geoid) {
304 return "/cgi-bin/thumb.pl?image="+im.id+"&g="+geoid+"&page="+im.articleId+"&f="+encodeURIComponent(im.image);
306 can_drag_and_drop: function() {
307 // hopefully they're implemented at the same time
308 if (window.FormData != null)
311 if (bse_use_file_api && window.FileReader != null)
316 make_drop_zone: function(options) {
317 options.element.addEventListener
320 function(options, e) {
323 }.bind(this, options),
326 options.element.addEventListener
329 function(options, e) {
332 }.bind(this, options),
335 options.element.addEventListener
338 function(options, e) {
342 options.onDrop(e.dataTransfer.files);
343 }.bind(this, options),
348 // image - file input element (required)
349 // id - owner article of the new image (required)
350 // name - name of the image to add (default: "")
351 // alt - alt text for the image (default: "")
352 // url - url for the image (default: "")
353 // storage - storage for the image (default: auto)
354 // onSuccess: called on success in adding the image, with the image object
356 // onFailure: called on failure (optional)
357 // onStart: called when the image upload starts (optional)
358 // onComplete: called when the image upload is complete (success
359 // or failure) (optional)
360 // onProgress: called occasionally during the image upload with
361 // the approximate amount sent and the total to be sent (optional)
362 add_image_file: function(parameters) {
363 parameters._csrfp = this._csrfp.admin_add_image;
364 var success = parameters.onSuccess;
365 parameters.onSuccess = function(success, result) {
366 success(result.image);
367 }.bind(this, success);
368 this._do_complex_request("/cgi-bin/admin/add.pl", "addimg", parameters);
370 save_image_file: function(parameters) {
371 parameters._csrfp = this._csrfp.admin_save_image;
372 var success = parameters.onSuccess;
373 parameters.onSuccess = function(success, result) {
374 success(result.image);
375 }.bind(this, success);
376 this._do_complex_request("/cgi-bin/admin/add.pl", "a_save_image", parameters);
378 remove_image_file: function(parameters) {
379 var success = parameters.onSuccess;
380 if (!success) this._badparm("remove_image_file() missing onSuccess parameter");
381 var failure = parameters.onFailure;
382 if (!failure) failure = this.onFailure;
383 var im = parameters.image;
384 if (!im) this._badparm("remove_image_file() missing image parameter");
394 images_set_order: function(parameters) {
395 var success = parameters.onSuccess;
396 if (!success) this._badparm("remove_image_file() missing onSuccess parameter");
397 var failure = parameters.onFailure;
398 if (!failure) failure = this.onFailure;
399 var id = parameters.id;
400 if (!id) this._badparm("images_set_order() missing id parameter");
401 var order = parameters.order.join(",");
402 this._do_add_request("a_order_images", { id: id, order: order }, success, failure);
405 // Message catalog functions
406 message_catalog: function(parameters) {
407 var success = parameters.onSuccess;
408 if (!success) this._badparm("remove_image_file() missing onSuccess parameter");
409 var failure = parameters.onFailure;
410 if (!failure) failure = this.onFailure;
413 "/cgi-bin/admin/messages.pl", "a_catalog", { },
414 function(success, resp) {
415 success(resp.messages);
416 }.bind(this, success),
420 message_detail: function(parameters) {
421 var success = parameters.onSuccess;
422 if (!success) this._badparm("message_detail() missing onSuccess parameter");
423 var failure = parameters.onFailure;
424 if (!failure) failure = this.onFailure;
425 var id = parameters.id;
426 if (id == null) this._badparm("message_detail() missing id parameter");
429 "/cgi-bin/admin/messages.pl", "a_detail", { id: id }, success, failure
432 // requires id, language_code, message
433 message_save: function(parameters) {
434 var success = parameters.onSuccess;
435 if (!success) this._badparm("message_save() missing onSuccess parameter");
436 var my_success = function(success, resp) {
437 success(resp.definition);
438 }.bind(this, success);
439 delete parameters.success;
440 var failure = parameters.onFailure;
441 if (!failure) failure = this.onFailure;
442 delete parameters.failure;
443 this._do_request("/cgi-bin/admin/messages.pl", "a_save", parameters,
444 my_success, failure);
446 // requires id, language_code
447 message_delete: function(parameters) {
448 var success = parameters.onSuccess;
449 if (!success) this._badparm("message_delete() missing onSuccess parameter");
450 delete parameters.success;
451 var failure = parameters.onFailure;
452 if (!failure) failure = this.onFailure;
453 delete parameters.failure;
454 this._do_request("/cgi-bin/admin/messages.pl", "a_delete", parameters,
458 // requires name, value
459 set_state: function(parameters) {
460 var success = parameters.onSuccess || function() {};
461 var failure = parameters.onFailure || this.onFailure;
462 delete parameters.onSuccess;
463 delete parameters.onFailure;
464 this._do_request("/cgi-bin/admin/menu.pl", "a_set_state", parameters, success, failure);
467 get_state: function(parameters) {
468 var success = parameters.onSuccess;
469 if (!success) this._badparm("get_state() missing onSuccess parameter");
470 var my_success = function(success, result) {
471 success(result.value);
472 }.bind(this, success);
473 var failure = parameters.onFailure || this.onFailure;
474 delete parameters.onSuccess;
475 delete parameters.onFailure;
476 this._do_request("/cgi-bin/admin/menu.pl", "a_get_state", parameters, my_success, failure);
479 delete_state: function(parameters) {
480 var success = parameters.onSuccess || function() {};
481 var failure = parameters.onFailure || this.onFailure;
482 delete parameters.onSuccess;
483 delete parameters.onFailure;
484 this._do_request("/cgi-bin/admin/menu.pl", "a_delete_state", parameters, success, failure);
487 // requires name, a prefix for the state entries we want
488 get_matching_state: function(parameters) {
489 var success = parameters.onSuccess;
490 if (!success) this._badparm("get_matching_state() missing onSuccess parameter");
491 var my_success = function(success, result) {
492 success(result.entries);
493 }.bind(this, success);
494 var failure = parameters.onFailure || this.onFailure;
495 delete parameters.onSuccess;
496 delete parameters.onFailure;
497 this._do_request("/cgi-bin/admin/menu.pl", "a_get_matching_state", parameters, my_success, failure);
501 // requires name, a prefix for the state entries we want
502 delete_matching_state: function(parameters) {
503 var success = parameters.onSuccess || function() {};
504 var failure = parameters.onFailure || this.onFailure;
505 delete parameters.onSuccess;
506 delete parameters.onFailure;
507 this._do_request("/cgi-bin/admin/menu.pl", "a_delete_matching_state", parameters, success, failure);
511 _progress_handler: function(parms) {
512 if (parms.finished) return;
513 this.get_file_progress(
515 _upload: parms.up_id,
516 onSuccess: function(parms, prog) {
517 if (!parms.finished) {
520 parms.total = prog.total;
521 parms.progress(prog);
524 parms.timeout = window.setTimeout
525 (this._progress_handler.bind(this, parms),
526 parms.updates > 5 ? 6000 : 1500);
531 _hidden: function(name, value) {
532 var hidden = document.createElement("input");
533 hidden.type = "hidden";
535 hidden.value = value;
539 _wrap_json_failure: function(resp) {
540 return resp.responseJSON;
542 _wrap_nojson_failure: function(resp) {
545 message: "Unexpected non-JSON response from server",
547 error_code: "NOTJSON"
550 _wrap_req_failure: function(resp) {
553 message: "Server error requesing content: " + resp.statusText,
555 error_code: "SERVFAIL"
558 _badparm: function(msg) {
559 this.onException(msg);
561 _add_complex_item: function(form, key, val, clone) {
562 if (typeof(val) == "string" || typeof(val) == "number") {
563 form.appendChild(this._hidden(key, val));
565 else if (typeof(val) == "object") {
566 if (val.constructor == Array) {
567 for (var i = 0; i < val.length; ++i) {
568 this._add_complex_item(form, key, val[i], clone);
572 // presumed to be a file field
574 var cloned = val.cloneNode(true);
575 val.parentNode.insertBefore(cloned, val);
578 form.appendChild(val);
582 _populate_complex_form: function(form, req_parms, clone) {
583 for (var key in req_parms) {
584 this._add_complex_item(form, key, req_parms[key], clone);
587 // perform a request through an iframe
588 // parameters can contain:
589 // onSuccess: callback called on successful processs
590 // onFailure: called on failed processing
591 // onStart: called when the form is submitted
592 // onProgress: called occasionally with submission progres info
593 // onComplete: called on completion (before onSuccess/onFailure)
594 // clone: if true, clone any file objects supplied
596 // all other parameters are treated as form fields.
597 // if a value is an array, it is treated as multiple values for
600 // Bugs: should fallback to Ajax if there are no form fields
601 _do_complex_request: function(url, action, parameters) {
602 var success = parameters.onSuccess;
603 if (!success) this._badparm("tree() missing onSuccess parameter");
604 var failure = parameters.onFailure;
605 if (!failure) failure = this.onFailure;
606 var on_complete = parameters.onComplete;
607 var on_start = parameters.onStart;
608 var on_progress = parameters.onProgress;
609 var clone = parameters.clone;
611 delete parameters.onSuccess;
612 delete parameters.onFailure;
613 delete parameters.onComplete;
614 delete parameters.onProgress;
615 delete parameters.onStart;
616 delete parameters.clone;
618 // stuff we use in the callbacks
624 progress: on_progress,
625 complete: on_complete,
626 // track the number of progress updates done
631 parms.up_id = this._new_upload_id();
636 url += "_upload=" + parms.up_id;
638 if (window.FormData) {
639 if (this._do_complex_formdata(url, action, parms, parameters))
643 if (window.FileReader && bse_use_file_api) {
644 if (this._do_complex_file_api(url, action, parms, parameters))
649 parms.ifr = new Element("iframe", {
651 id: "bseiframe"+parms.up_id,
652 name: "bseiframe"+parms.up_id,
656 parms.ifr.style.display = "none";
659 var form = new Element
664 enctype: "multipart/form-data",
665 // the following for IE
666 encoding: "multipart/form-data",
667 id: "bseform"+parms.up_id,
668 target: "bseiframe"+parms.up_id
671 form.style.display = "none";
672 // _upload must come before anything large
673 //form.appendChild(this._hidden("_upload", parms.up_id));
674 form.appendChild(this._hidden("_",1));
675 this._populate_complex_form(form, parameters, clone);
676 // trigger BSE's alternative JSON return handling
677 form.appendChild(this._hidden("_", 1));
678 form.appendChild(this._hidden(action, 1));
680 document.body.appendChild(parms.ifr);
681 document.body.appendChild(form);
682 var onLoad = function(parms) {
683 // we should get json back in the body
685 var form = parms.form;
686 var text = Try.these(
688 var text = ifr.contentDocument.body.textContent;
689 ifr.contentDocument.close();
693 var text = ifr.contentWindow.document.body.innerText;
694 ifr.contentWindow.document.close();
699 eval("data = " + text + ";");
700 document.body.removeChild(ifr);
701 document.body.removeChild(form);
702 if (parms.progress != null && parms.total != null)
703 parms.progress({ done: parms.total, total: parms.total});
704 if (parms.complete != null)
708 if (data.success != null && data.success != 0) {
716 parms.failure(this._wrap_req_failure({statusText: "Unknown"}));
719 if (window.attachEvent) {
720 parms.ifr.attachEvent("onload", onLoad);
723 parms.ifr.addEventListener("load", onLoad, false);
726 if (on_start != null)
729 if (on_progress != null) {
730 parms.timeout = window.setTimeout ( this._progress_handler.bind(this, parms), 200 );
735 // flatten the parameters
736 _flat_parms: function(flat, key, val) {
737 if (typeof(val) == "string" || typeof(val) == "number") {
738 flat.push([ key, val, false ]);
740 else if (typeof(val) == "object") {
741 if (val.constructor == Array) {
742 for (var i = 0; i < val.length; ++i) {
743 this._flat_parms(flat, key, val[i]);
746 else if (val.constructor == File) {
747 // File object from drag and drop
748 flat.push([key, val, true]);
751 // this should handle File objects, not just elements
752 // or perhaps data transfer objects
753 // push the individual files if there's multiple
754 for (var i = 0; i < val.files.length; ++i) {
755 flat.push([key, val.files[i], true]);
760 _file_progress_event: function(state, evt) {
761 if (evt.lengthComputable) {
763 for (var i = 0; i < state.fileoffsets.length; ++i) {
764 if (evt.loaded > state.fileoffsets[i][0])
765 filename = state.fileoffsets[i][1];
767 state.last_filename = filename;
779 _file_load_event: function(state, evt) {
780 if (evt.lengthComputable) {
786 filename: state.last_filename,
792 _file_readystatechange_event: function(state, event) {
793 if (state.xhr.readyState == 4) {
796 if (state.xhr.status == 200) {
799 data = state.xhr.responseText.evalJSON(false);
801 state.failure(this._wrap_nojson_failure(state.xhr));
805 if (data.success != null && data.success != 0 ) {
809 state.failure(this._wrap_json_failure({ responseJSON: data}));
813 state.failure(this._wrap_req_failure(state.xhr));
817 _build_api_req_data: function(state) {
818 while (state.index < state.flat.length) {
819 var entry = state.flat[state.index];
822 var fr = new FileReader;
824 ("loadend", function(state, fr, event) {
825 var entry = state.flat[state.index];
826 state.req_data += "--" + state.sep + "\r\n";
827 // TODO: filenames with quotes
828 state.fileoffsets.push([ state.req_data.length, entry[1].fileName]);
829 state.req_data += "Content-Disposition: form-data; name=\"" + entry[0] + "\"; filename=\"" + this._encode_utf8(entry[1].fileName) + "\"\r\n\r\n";
830 state.req_data += event.target.result + "\r\n";
832 this._build_api_req_data(state);
833 }.bind(this, state, fr), false);
834 fr.readAsBinaryString(entry[1]);
839 state.req_data += "--" + state.sep;
840 state.req_data += "Content-Disposition: form-data; name=\"" + entry[0] + "\"\r\n\r\n";
841 state.req_data += this._encode_utf8(entry[1]) + "\r\n";
846 // everything should be state.req_data now
847 state.req_data += "--" + state.sep + "--\r\n";
849 state.xhr = new XMLHttpRequest();
852 if (state.progress && state.xhr.upload) {
853 state.xhr.upload.addEventListener
856 this._file_progress_event.bind(this, state),
859 state.xhr.upload.addEventListener
862 this._file_load_event.bind(this, state),
866 state.xhr.open("POST", state.url, true);
867 state.xhr.onreadystatechange = this._file_readystatechange_event.bind(this, state);
868 state.xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary="+state.sep);
869 state.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
870 state.xhr.sendAsBinary(state.req_data);
872 // use the HTML5 file API to perform the upload
873 _do_complex_file_api: function(url, action, state, req_parms) {
875 //state.url = "/cgi-bin/dump.pl";
877 req_parms[action] = 1;
878 state.sep = "x" + state.up_id + "x";
879 state.fileoffsets = new Array;
881 // flatten the request parameters
882 var flat = new Array;
883 for (var key in req_parms) {
884 this._flat_parms(flat, key, req_parms[key]);
889 this._build_api_req_data(state);
890 // the rest happens elsewhere
894 _do_complex_formdata: function(url, action, state, params) {
899 state.fileoffsets = new Array();
902 var fd = new FormData();
903 for (var key in params) {
904 var val = params[key];
905 if (typeof(val) == "string" || typeof(val) == "number") {
909 if (val.constructor == Array) {
910 for (var i = 0; i < val.length; ++i) {
914 else if (val.constructor == File) {
919 // hopefully a file input
920 for (var i = 0; i < val.files.length; ++i) {
921 var file = val.files[i];
922 state.fileoffsets.push([ offset, file.fileName ]);
923 fd.append(key, file);
924 offset += file.fileSize;
930 // FIXME: duplicate code (mostly)
931 state.xhr = new XMLHttpRequest();
934 if (state.progress && state.xhr.upload) {
935 state.xhr.upload.addEventListener
938 this._file_progress_event.bind(this, state),
941 state.xhr.upload.addEventListener
944 this._file_load_event.bind(this, state),
948 state.xhr.open("POST", state.url, true);
949 state.xhr.onreadystatechange = this._file_readystatechange_event.bind(this, state);
951 //state.xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary="+state.sep);
952 state.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
957 // in the future this might call a proxy
958 _do_add_request: function(action, other_parms, success, failure) {
959 this._do_request("/cgi-bin/admin/add.pl", action, other_parms, success, failure);
961 _do_api_request: function(action, other_parms, success, failure) {
962 this._do_request("/cgi-bin/api.pl", action, other_parms, success, failure);
964 _do_request: function(url, action, other_parms, success, failure) {
966 other_parms[action] = 1;
967 new Ajax.Request(url,
969 parameters: other_parms,
970 onSuccess: function (success, failure, resp) {
971 if (resp.responseJSON) {
972 if (resp.responseJSON.success != null && resp.responseJSON.success != 0) {
973 success(resp.responseJSON);
976 failure(this._wrap_json_failure(resp), resp);
980 failure(this._wrap_nojson_failure(resp), resp);
982 }.bind(this, success, failure),
983 onFailure: function(failure, resp) {
984 failure(this._wrap_req_failure(resp), resp);
985 }.bind(this, failure),
986 onException: this.onException
989 _new_upload_id: function () {
990 this._upload_id += 1;
991 return new Date().valueOf() + "_" + this._upload_id;
993 _encode_utf8: function(str) {
994 return unescape(encodeURIComponent(str));
996 // we request these names on startup, on login
997 // and occasionally otherwise, to avoid them going stale