PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/ festos/js/jquery.ajax_upload.js

http://festos.googlecode.com/
JavaScript | 304 lines | 191 code | 42 blank | 71 comment | 21 complexity | d97452e3b423af98e9a52629ff130280 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause
  1. // Copyright (c) 2008 Andris Valums, http://valums.com
  2. // Licensed under the MIT license (http://valums.com/mit-license/)
  3. // Thanks to Loic Fontaine, Mark Feldman, Andras Popovics, Faisal for contribution
  4. /*
  5. See http://valums.com/projects/ajax-upload/ for documentation
  6. Changelog:
  7. Version 0.6 - Fixed bugs:
  8. 1. Disabling button while uploading resulted in empty upload
  9. 2. Submitting empty file input in Chrome, when user clicked cancel
  10. */
  11. (function($){
  12. // we need jQuery to run
  13. if ( ! $) return;
  14. $.ajax_upload = function(button, options){
  15. // make sure it is jquery object
  16. button = $(button);
  17. if (button.size() != 1 ){
  18. console.error('You passed ', button.size(),' elements to ajax_upload at once');
  19. return false;
  20. }
  21. return new Ajax_upload(button, options);
  22. };
  23. /**
  24. * Function generates unique id
  25. * @return unique id
  26. */
  27. var get_uid = function(){
  28. var uid = 0;
  29. return function(){
  30. return uid++;
  31. }
  32. }();
  33. /**
  34. * @param button Element that will be used as file upload button
  35. * @param option User options
  36. */
  37. var Ajax_upload = function(button, options){
  38. this.button = button;
  39. this.wrapper = null;
  40. this.form = null;
  41. this.input = null;
  42. this.iframe = null;
  43. this.disabled = false;
  44. this.submitting = false;
  45. this.settings = {
  46. // Location of the server-side upload script
  47. action: 'upload.php',
  48. // File upload name
  49. name: 'userfile',
  50. // Additional data to send
  51. data: {},
  52. // Fired when user selects file
  53. // You can return false to cancel upload
  54. onSubmit: function(file, extension) {},
  55. // Fired when file upload is completed
  56. onComplete: function(file, response) {},
  57. // Fired when server returns the "success" string
  58. onSuccess: function(file){},
  59. // Fired when server return something else
  60. onError: function(file, response){}
  61. };
  62. // Merge the users options with our defaults
  63. $.extend(this.settings, options);
  64. this.create_wrapper();
  65. this.create_input();
  66. if (jQuery.browser.msie){
  67. // fix ie transparent background bug
  68. this.make_parent_opaque();
  69. }
  70. this.create_iframe();
  71. }
  72. // assigning methods to our class
  73. Ajax_upload.prototype = {
  74. set_data : function(data){
  75. this.settings.data = data;
  76. },
  77. disable : function(){
  78. this.disabled = true;
  79. if ( ! this.submitting){
  80. this.input.attr('disabled', true);
  81. }
  82. },
  83. enable : function(){
  84. this.disabled = false;
  85. this.input.attr('disabled', false);
  86. },
  87. /**
  88. * Creates wrapper for button and invisible file input
  89. */
  90. create_wrapper : function(){
  91. // Shorten names
  92. var button = this.button, wrapper;
  93. wrapper = this.wrapper = $('<div></div>')
  94. .insertAfter(button)
  95. .append(button);
  96. // wait a bit because of FF bug
  97. // it can't properly calculate the outerHeight
  98. setTimeout(function(){
  99. wrapper.css({
  100. position: 'relative'
  101. ,display: 'block'
  102. ,overflow: 'hidden'
  103. ,height: button.outerHeight(true)
  104. ,width: button.outerWidth(true)
  105. });
  106. }, 1);
  107. var self = this;
  108. wrapper.mousemove(function(e){
  109. // Move the input with the mouse, so the user can't misclick it
  110. if (!self.input) {
  111. return;
  112. }
  113. self.input.css({
  114. top: e.pageY - wrapper.offset().top - 5 + 'px'
  115. ,left: e.pageX - wrapper.offset().left - 170 + 'px'
  116. });
  117. });
  118. },
  119. /**
  120. * Creates invisible file input above the button
  121. */
  122. create_input : function(){
  123. var self = this;
  124. this.input =
  125. $('<input type="file" />')
  126. .attr('name', this.settings.name)
  127. .css({
  128. 'position' : 'absolute'
  129. ,'margin': 0
  130. ,'padding': 0
  131. ,'width': '220px'
  132. ,'heigth': '10px'
  133. ,'opacity': 0
  134. })
  135. .change(function(){
  136. if ($(this).val() == ''){
  137. // there is no file
  138. return;
  139. }
  140. // we need to lock "disable" method
  141. self.submitting = true;
  142. // Submit form when value is changed
  143. self.submit();
  144. // unlock "disable" method
  145. self.submitting = false;
  146. })
  147. .appendTo(this.wrapper)
  148. // Emulate button hover effect
  149. .hover(
  150. function(){self.button.addClass('hover');}
  151. ,function(){self.button.removeClass('hover');}
  152. );
  153. if (this.disabled){
  154. this.input.attr('disabled', true);
  155. }
  156. },
  157. /**
  158. * Creates iframe with unique name
  159. */
  160. create_iframe : function(){
  161. // unique name
  162. // We cannot use getTime, because it sometimes return
  163. // same value in safari :(
  164. var name = 'iframe_au' + get_uid();
  165. // create iframe, so we dont need to refresh page
  166. this.iframe =
  167. $('<iframe name="' + name + '"></iframe>')
  168. .css('display', 'none')
  169. .appendTo('body');
  170. },
  171. /**
  172. * Upload file without refreshing the page
  173. */
  174. submit : function(){
  175. var self = this, settings = this.settings;
  176. // get filename from input
  177. var file = this.file_from_path(this.input.val());
  178. // execute user event
  179. if (settings.onSubmit.call(this, file, this.get_ext(file)) === false){
  180. // Do not continue if user function returns false
  181. if (self.disabled){
  182. this.input.attr('disabled', true);
  183. }
  184. return;
  185. }
  186. this.create_form();
  187. this.input.appendTo(this.form);
  188. this.form.submit();
  189. this.input.remove(); this.input = null;
  190. this.form.remove(); this.form = null;
  191. this.submitting = false;
  192. // create new input
  193. this.create_input();
  194. var iframe = this.iframe;
  195. iframe.load(function(){
  196. var response = iframe.contents().find('body').html();
  197. settings.onComplete.call(self, file, response);
  198. if (response == 'success'){
  199. settings.onSuccess.call(self, file);
  200. } else {
  201. settings.onError.call(self, file, response);
  202. }
  203. // clear (wait a bit because of FF2 bug)
  204. setTimeout(function(){
  205. iframe.remove();
  206. }, 1);
  207. });
  208. // Create new iframe, so we can have multiple uploads at once
  209. this.create_iframe();
  210. },
  211. /**
  212. * Creates form, that will be submitted to iframe
  213. */
  214. create_form : function(){
  215. // enctype must be specified here
  216. // because changing this attr on the fly is not allowed
  217. this.form =
  218. $('<form method="post" enctype="multipart/form-data"></form>')
  219. .appendTo('body')
  220. .attr({
  221. "action" : this.settings.action
  222. ,"target" : this.iframe.attr('name')
  223. });
  224. // Create hidden input element for each data key
  225. for (var i in this.settings.data){
  226. $('<input type="hidden" />')
  227. .appendTo(this.form)
  228. .attr({
  229. 'name': i
  230. ,'value': this.settings.data[i]
  231. });
  232. }
  233. },
  234. file_from_path : function(file){
  235. var i = file.lastIndexOf('\\');
  236. if (i !== -1 ){
  237. return file.slice(i+1);
  238. }
  239. return file;
  240. },
  241. get_ext : function(file){
  242. var i = file.lastIndexOf('.');
  243. if (i !== -1 ){
  244. return file.slice(i+1);
  245. }
  246. return '';
  247. },
  248. make_parent_opaque : function(){
  249. // ie transparent background bug
  250. this.button.add(this.button.parents()).each(function(){
  251. var color = $(this).css('backgroundColor');
  252. var image = $(this).css('backgroundImage');
  253. if ( color != 'transparent' || image != 'none'){
  254. $(this).css('opacity', 1);
  255. return false;
  256. }
  257. });
  258. }
  259. };
  260. })(jQuery);