plupload.dev.js 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316
  1. /**
  2. * Plupload - multi-runtime File Uploader
  3. * v2.1.2
  4. *
  5. * Copyright 2013, Moxiecode Systems AB
  6. * Released under GPL License.
  7. *
  8. * License: http://www.plupload.com/license
  9. * Contributing: http://www.plupload.com/contributing
  10. *
  11. * Date: 2014-05-14
  12. */
  13. /**
  14. * Plupload.js
  15. *
  16. * Copyright 2013, Moxiecode Systems AB
  17. * Released under GPL License.
  18. *
  19. * License: http://www.plupload.com/license
  20. * Contributing: http://www.plupload.com/contributing
  21. */
  22. /*global mOxie:true */
  23. ;(function(window, o, undef) {
  24. var delay = window.setTimeout
  25. , fileFilters = {}
  26. ;
  27. // convert plupload features to caps acceptable by mOxie
  28. function normalizeCaps(settings) {
  29. var features = settings.required_features, caps = {};
  30. function resolve(feature, value, strict) {
  31. // Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
  32. var map = {
  33. chunks: 'slice_blob',
  34. jpgresize: 'send_binary_string',
  35. pngresize: 'send_binary_string',
  36. progress: 'report_upload_progress',
  37. multi_selection: 'select_multiple',
  38. dragdrop: 'drag_and_drop',
  39. drop_element: 'drag_and_drop',
  40. headers: 'send_custom_headers',
  41. urlstream_upload: 'send_binary_string',
  42. canSendBinary: 'send_binary',
  43. triggerDialog: 'summon_file_dialog'
  44. };
  45. if (map[feature]) {
  46. caps[map[feature]] = value;
  47. } else if (!strict) {
  48. caps[feature] = value;
  49. }
  50. }
  51. if (typeof(features) === 'string') {
  52. plupload.each(features.split(/\s*,\s*/), function(feature) {
  53. resolve(feature, true);
  54. });
  55. } else if (typeof(features) === 'object') {
  56. plupload.each(features, function(value, feature) {
  57. resolve(feature, value);
  58. });
  59. } else if (features === true) {
  60. // check settings for required features
  61. if (settings.chunk_size > 0) {
  62. caps.slice_blob = true;
  63. }
  64. if (settings.resize.enabled || !settings.multipart) {
  65. caps.send_binary_string = true;
  66. }
  67. plupload.each(settings, function(value, feature) {
  68. resolve(feature, !!value, true); // strict check
  69. });
  70. }
  71. return caps;
  72. }
  73. /**
  74. * @module plupload
  75. * @static
  76. */
  77. var plupload = {
  78. /**
  79. * Plupload version will be replaced on build.
  80. *
  81. * @property VERSION
  82. * @for Plupload
  83. * @static
  84. * @final
  85. */
  86. VERSION : '2.1.2',
  87. /**
  88. * Inital state of the queue and also the state ones it's finished all it's uploads.
  89. *
  90. * @property STOPPED
  91. * @static
  92. * @final
  93. */
  94. STOPPED : 1,
  95. /**
  96. * Upload process is running
  97. *
  98. * @property STARTED
  99. * @static
  100. * @final
  101. */
  102. STARTED : 2,
  103. /**
  104. * File is queued for upload
  105. *
  106. * @property QUEUED
  107. * @static
  108. * @final
  109. */
  110. QUEUED : 1,
  111. /**
  112. * File is being uploaded
  113. *
  114. * @property UPLOADING
  115. * @static
  116. * @final
  117. */
  118. UPLOADING : 2,
  119. /**
  120. * File has failed to be uploaded
  121. *
  122. * @property FAILED
  123. * @static
  124. * @final
  125. */
  126. FAILED : 4,
  127. /**
  128. * File has been uploaded successfully
  129. *
  130. * @property DONE
  131. * @static
  132. * @final
  133. */
  134. DONE : 5,
  135. // Error constants used by the Error event
  136. /**
  137. * Generic error for example if an exception is thrown inside Silverlight.
  138. *
  139. * @property GENERIC_ERROR
  140. * @static
  141. * @final
  142. */
  143. GENERIC_ERROR : -100,
  144. /**
  145. * HTTP transport error. For example if the server produces a HTTP status other than 200.
  146. *
  147. * @property HTTP_ERROR
  148. * @static
  149. * @final
  150. */
  151. HTTP_ERROR : -200,
  152. /**
  153. * Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
  154. *
  155. * @property IO_ERROR
  156. * @static
  157. * @final
  158. */
  159. IO_ERROR : -300,
  160. /**
  161. * @property SECURITY_ERROR
  162. * @static
  163. * @final
  164. */
  165. SECURITY_ERROR : -400,
  166. /**
  167. * Initialization error. Will be triggered if no runtime was initialized.
  168. *
  169. * @property INIT_ERROR
  170. * @static
  171. * @final
  172. */
  173. INIT_ERROR : -500,
  174. /**
  175. * File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered.
  176. *
  177. * @property FILE_SIZE_ERROR
  178. * @static
  179. * @final
  180. */
  181. FILE_SIZE_ERROR : -600,
  182. /**
  183. * File extension error. If the user selects a file that isn't valid according to the filters setting.
  184. *
  185. * @property FILE_EXTENSION_ERROR
  186. * @static
  187. * @final
  188. */
  189. FILE_EXTENSION_ERROR : -601,
  190. /**
  191. * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
  192. *
  193. * @property FILE_DUPLICATE_ERROR
  194. * @static
  195. * @final
  196. */
  197. FILE_DUPLICATE_ERROR : -602,
  198. /**
  199. * Runtime will try to detect if image is proper one. Otherwise will throw this error.
  200. *
  201. * @property IMAGE_FORMAT_ERROR
  202. * @static
  203. * @final
  204. */
  205. IMAGE_FORMAT_ERROR : -700,
  206. /**
  207. * While working on files runtime may run out of memory and will throw this error.
  208. *
  209. * @since 2.1.2
  210. * @property MEMORY_ERROR
  211. * @static
  212. * @final
  213. */
  214. MEMORY_ERROR : -701,
  215. /**
  216. * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
  217. *
  218. * @property IMAGE_DIMENSIONS_ERROR
  219. * @static
  220. * @final
  221. */
  222. IMAGE_DIMENSIONS_ERROR : -702,
  223. /**
  224. * Mime type lookup table.
  225. *
  226. * @property mimeTypes
  227. * @type Object
  228. * @final
  229. */
  230. mimeTypes : o.mimes,
  231. /**
  232. * In some cases sniffing is the only way around :(
  233. */
  234. ua: o.ua,
  235. /**
  236. * Gets the true type of the built-in object (better version of typeof).
  237. * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
  238. *
  239. * @method typeOf
  240. * @static
  241. * @param {Object} o Object to check.
  242. * @return {String} Object [[Class]]
  243. */
  244. typeOf: o.typeOf,
  245. /**
  246. * Extends the specified object with another object.
  247. *
  248. * @method extend
  249. * @static
  250. * @param {Object} target Object to extend.
  251. * @param {Object..} obj Multiple objects to extend with.
  252. * @return {Object} Same as target, the extended object.
  253. */
  254. extend : o.extend,
  255. /**
  256. * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
  257. * The only way a user would be able to get the same ID is if the two persons at the same exact milisecond manages
  258. * to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique.
  259. * It's more probable for the earth to be hit with an ansteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property
  260. * to an user unique key.
  261. *
  262. * @method guid
  263. * @static
  264. * @return {String} Virtually unique id.
  265. */
  266. guid : o.guid,
  267. /**
  268. * Get array of DOM Elements by their ids.
  269. *
  270. * @method get
  271. * @for Utils
  272. * @param {String} id Identifier of the DOM Element
  273. * @return {Array}
  274. */
  275. get : function get(ids) {
  276. var els = [], el;
  277. if (o.typeOf(ids) !== 'array') {
  278. ids = [ids];
  279. }
  280. var i = ids.length;
  281. while (i--) {
  282. el = o.get(ids[i]);
  283. if (el) {
  284. els.push(el);
  285. }
  286. }
  287. return els.length ? els : null;
  288. },
  289. /**
  290. * Executes the callback function for each item in array/object. If you return false in the
  291. * callback it will break the loop.
  292. *
  293. * @method each
  294. * @static
  295. * @param {Object} obj Object to iterate.
  296. * @param {function} callback Callback function to execute for each item.
  297. */
  298. each : o.each,
  299. /**
  300. * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
  301. *
  302. * @method getPos
  303. * @static
  304. * @param {Element} node HTML element or element id to get x, y position from.
  305. * @param {Element} root Optional root element to stop calculations at.
  306. * @return {object} Absolute position of the specified element object with x, y fields.
  307. */
  308. getPos : o.getPos,
  309. /**
  310. * Returns the size of the specified node in pixels.
  311. *
  312. * @method getSize
  313. * @static
  314. * @param {Node} node Node to get the size of.
  315. * @return {Object} Object with a w and h property.
  316. */
  317. getSize : o.getSize,
  318. /**
  319. * Encodes the specified string.
  320. *
  321. * @method xmlEncode
  322. * @static
  323. * @param {String} s String to encode.
  324. * @return {String} Encoded string.
  325. */
  326. xmlEncode : function(str) {
  327. var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g;
  328. return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
  329. return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
  330. }) : str;
  331. },
  332. /**
  333. * Forces anything into an array.
  334. *
  335. * @method toArray
  336. * @static
  337. * @param {Object} obj Object with length field.
  338. * @return {Array} Array object containing all items.
  339. */
  340. toArray : o.toArray,
  341. /**
  342. * Find an element in array and return it's index if present, otherwise return -1.
  343. *
  344. * @method inArray
  345. * @static
  346. * @param {mixed} needle Element to find
  347. * @param {Array} array
  348. * @return {Int} Index of the element, or -1 if not found
  349. */
  350. inArray : o.inArray,
  351. /**
  352. * Extends the language pack object with new items.
  353. *
  354. * @method addI18n
  355. * @static
  356. * @param {Object} pack Language pack items to add.
  357. * @return {Object} Extended language pack object.
  358. */
  359. addI18n : o.addI18n,
  360. /**
  361. * Translates the specified string by checking for the english string in the language pack lookup.
  362. *
  363. * @method translate
  364. * @static
  365. * @param {String} str String to look for.
  366. * @return {String} Translated string or the input string if it wasn't found.
  367. */
  368. translate : o.translate,
  369. /**
  370. * Checks if object is empty.
  371. *
  372. * @method isEmptyObj
  373. * @static
  374. * @param {Object} obj Object to check.
  375. * @return {Boolean}
  376. */
  377. isEmptyObj : o.isEmptyObj,
  378. /**
  379. * Checks if specified DOM element has specified class.
  380. *
  381. * @method hasClass
  382. * @static
  383. * @param {Object} obj DOM element like object to add handler to.
  384. * @param {String} name Class name
  385. */
  386. hasClass : o.hasClass,
  387. /**
  388. * Adds specified className to specified DOM element.
  389. *
  390. * @method addClass
  391. * @static
  392. * @param {Object} obj DOM element like object to add handler to.
  393. * @param {String} name Class name
  394. */
  395. addClass : o.addClass,
  396. /**
  397. * Removes specified className from specified DOM element.
  398. *
  399. * @method removeClass
  400. * @static
  401. * @param {Object} obj DOM element like object to add handler to.
  402. * @param {String} name Class name
  403. */
  404. removeClass : o.removeClass,
  405. /**
  406. * Returns a given computed style of a DOM element.
  407. *
  408. * @method getStyle
  409. * @static
  410. * @param {Object} obj DOM element like object.
  411. * @param {String} name Style you want to get from the DOM element
  412. */
  413. getStyle : o.getStyle,
  414. /**
  415. * Adds an event handler to the specified object and store reference to the handler
  416. * in objects internal Plupload registry (@see removeEvent).
  417. *
  418. * @method addEvent
  419. * @static
  420. * @param {Object} obj DOM element like object to add handler to.
  421. * @param {String} name Name to add event listener to.
  422. * @param {Function} callback Function to call when event occurs.
  423. * @param {String} (optional) key that might be used to add specifity to the event record.
  424. */
  425. addEvent : o.addEvent,
  426. /**
  427. * Remove event handler from the specified object. If third argument (callback)
  428. * is not specified remove all events with the specified name.
  429. *
  430. * @method removeEvent
  431. * @static
  432. * @param {Object} obj DOM element to remove event listener(s) from.
  433. * @param {String} name Name of event listener to remove.
  434. * @param {Function|String} (optional) might be a callback or unique key to match.
  435. */
  436. removeEvent: o.removeEvent,
  437. /**
  438. * Remove all kind of events from the specified object
  439. *
  440. * @method removeAllEvents
  441. * @static
  442. * @param {Object} obj DOM element to remove event listeners from.
  443. * @param {String} (optional) unique key to match, when removing events.
  444. */
  445. removeAllEvents: o.removeAllEvents,
  446. /**
  447. * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
  448. *
  449. * @method cleanName
  450. * @static
  451. * @param {String} s String to clean up.
  452. * @return {String} Cleaned string.
  453. */
  454. cleanName : function(name) {
  455. var i, lookup;
  456. // Replace diacritics
  457. lookup = [
  458. /[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
  459. /\307/g, 'C', /\347/g, 'c',
  460. /[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
  461. /[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
  462. /\321/g, 'N', /\361/g, 'n',
  463. /[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
  464. /[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
  465. ];
  466. for (i = 0; i < lookup.length; i += 2) {
  467. name = name.replace(lookup[i], lookup[i + 1]);
  468. }
  469. // Replace whitespace
  470. name = name.replace(/\s+/g, '_');
  471. // Remove anything else
  472. name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
  473. return name;
  474. },
  475. /**
  476. * Builds a full url out of a base URL and an object with items to append as query string items.
  477. *
  478. * @method buildUrl
  479. * @static
  480. * @param {String} url Base URL to append query string items to.
  481. * @param {Object} items Name/value object to serialize as a querystring.
  482. * @return {String} String with url + serialized query string items.
  483. */
  484. buildUrl : function(url, items) {
  485. var query = '';
  486. plupload.each(items, function(value, name) {
  487. query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
  488. });
  489. if (query) {
  490. url += (url.indexOf('?') > 0 ? '&' : '?') + query;
  491. }
  492. return url;
  493. },
  494. /**
  495. * Formats the specified number as a size string for example 1024 becomes 1 KB.
  496. *
  497. * @method formatSize
  498. * @static
  499. * @param {Number} size Size to format as string.
  500. * @return {String} Formatted size string.
  501. */
  502. formatSize : function(size) {
  503. if (size === undef || /\D/.test(size)) {
  504. return plupload.translate('N/A');
  505. }
  506. function round(num, precision) {
  507. return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
  508. }
  509. var boundary = Math.pow(1024, 4);
  510. // TB
  511. if (size > boundary) {
  512. return round(size / boundary, 1) + " " + plupload.translate('tb');
  513. }
  514. // GB
  515. if (size > (boundary/=1024)) {
  516. return round(size / boundary, 1) + " " + plupload.translate('gb');
  517. }
  518. // MB
  519. if (size > (boundary/=1024)) {
  520. return round(size / boundary, 1) + " " + plupload.translate('mb');
  521. }
  522. // KB
  523. if (size > 1024) {
  524. return Math.round(size / 1024) + " " + plupload.translate('kb');
  525. }
  526. return size + " " + plupload.translate('b');
  527. },
  528. /**
  529. * Parses the specified size string into a byte value. For example 10kb becomes 10240.
  530. *
  531. * @method parseSize
  532. * @static
  533. * @param {String|Number} size String to parse or number to just pass through.
  534. * @return {Number} Size in bytes.
  535. */
  536. parseSize : o.parseSizeStr,
  537. /**
  538. * A way to predict what runtime will be choosen in the current environment with the
  539. * specified settings.
  540. *
  541. * @method predictRuntime
  542. * @static
  543. * @param {Object|String} config Plupload settings to check
  544. * @param {String} [runtimes] Comma-separated list of runtimes to check against
  545. * @return {String} Type of compatible runtime
  546. */
  547. predictRuntime : function(config, runtimes) {
  548. var up, runtime;
  549. up = new plupload.Uploader(config);
  550. runtime = o.Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
  551. up.destroy();
  552. return runtime;
  553. },
  554. /**
  555. * Registers a filter that will be executed for each file added to the queue.
  556. * If callback returns false, file will not be added.
  557. *
  558. * Callback receives two arguments: a value for the filter as it was specified in settings.filters
  559. * and a file to be filtered. Callback is executed in the context of uploader instance.
  560. *
  561. * @method addFileFilter
  562. * @static
  563. * @param {String} name Name of the filter by which it can be referenced in settings.filters
  564. * @param {String} cb Callback - the actual routine that every added file must pass
  565. */
  566. addFileFilter: function(name, cb) {
  567. fileFilters[name] = cb;
  568. }
  569. };
  570. plupload.addFileFilter('mime_types', function(filters, file, cb) {
  571. if (filters.length && !filters.regexp.test(file.name)) {
  572. this.trigger('Error', {
  573. code : plupload.FILE_EXTENSION_ERROR,
  574. message : plupload.translate('File extension error.'),
  575. file : file
  576. });
  577. cb(false);
  578. } else {
  579. cb(true);
  580. }
  581. });
  582. plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
  583. var undef;
  584. maxSize = plupload.parseSize(maxSize);
  585. // Invalid file size
  586. if (file.size !== undef && maxSize && file.size > maxSize) {
  587. this.trigger('Error', {
  588. code : plupload.FILE_SIZE_ERROR,
  589. message : plupload.translate('File size error.'),
  590. file : file
  591. });
  592. cb(false);
  593. } else {
  594. cb(true);
  595. }
  596. });
  597. plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
  598. if (value) {
  599. var ii = this.files.length;
  600. while (ii--) {
  601. // Compare by name and size (size might be 0 or undefined, but still equivalent for both)
  602. if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
  603. this.trigger('Error', {
  604. code : plupload.FILE_DUPLICATE_ERROR,
  605. message : plupload.translate('Duplicate file error.'),
  606. file : file
  607. });
  608. cb(false);
  609. return;
  610. }
  611. }
  612. }
  613. cb(true);
  614. });
  615. /**
  616. @class Uploader
  617. @constructor
  618. @param {Object} settings For detailed information about each option check documentation.
  619. @param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
  620. @param {String} settings.url URL of the server-side upload handler.
  621. @param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled.
  622. @param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
  623. @param {String} [settings.container] id of the DOM element to use as a container for uploader structures. Defaults to document.body.
  624. @param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop.
  625. @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
  626. @param {Object} [settings.filters={}] Set of file type filters.
  627. @param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR`
  628. @param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`.
  629. @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
  630. @param {String} [settings.flash_swf_url] URL of the Flash swf.
  631. @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
  632. @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
  633. @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
  634. @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
  635. @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
  636. @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
  637. @param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}`
  638. @param {Number} [settings.resize.width] If image is bigger, it will be resized.
  639. @param {Number} [settings.resize.height] If image is bigger, it will be resized.
  640. @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
  641. @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
  642. @param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
  643. @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
  644. @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
  645. @param {Boolean} [settings.send_file_name=true] Whether to send file name as additional argument - 'name' (required for chunked uploads and some other cases where file name cannot be sent via normal ways).
  646. */
  647. plupload.Uploader = function(options) {
  648. /**
  649. * Fires when the current RunTime has been initialized.
  650. *
  651. * @event Init
  652. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  653. */
  654. /**
  655. * Fires after the init event incase you need to perform actions there.
  656. *
  657. * @event PostInit
  658. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  659. */
  660. /**
  661. * Fires when the option is changed in via uploader.setOption().
  662. *
  663. * @event OptionChanged
  664. * @since 2.1
  665. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  666. * @param {String} name Name of the option that was changed
  667. * @param {Mixed} value New value for the specified option
  668. * @param {Mixed} oldValue Previous value of the option
  669. */
  670. /**
  671. * Fires when the silverlight/flash or other shim needs to move.
  672. *
  673. * @event Refresh
  674. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  675. */
  676. /**
  677. * Fires when the overall state is being changed for the upload queue.
  678. *
  679. * @event StateChanged
  680. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  681. */
  682. /**
  683. * Fires when browse_button is clicked and browse dialog shows.
  684. *
  685. * @event Browse
  686. * @since 2.1.2
  687. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  688. */
  689. /**
  690. * Fires for every filtered file before it is added to the queue.
  691. *
  692. * @event FileFiltered
  693. * @since 2.1
  694. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  695. * @param {plupload.File} file Another file that has to be added to the queue.
  696. */
  697. /**
  698. * Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
  699. *
  700. * @event QueueChanged
  701. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  702. */
  703. /**
  704. * Fires after files were filtered and added to the queue.
  705. *
  706. * @event FilesAdded
  707. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  708. * @param {Array} files Array of file objects that were added to queue by the user.
  709. */
  710. /**
  711. * Fires when file is removed from the queue.
  712. *
  713. * @event FilesRemoved
  714. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  715. * @param {Array} files Array of files that got removed.
  716. */
  717. /**
  718. * Fires when just before a file is uploaded. This event enables you to override settings
  719. * on the uploader instance before the file is uploaded.
  720. *
  721. * @event BeforeUpload
  722. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  723. * @param {plupload.File} file File to be uploaded.
  724. */
  725. /**
  726. * Fires when a file is to be uploaded by the runtime.
  727. *
  728. * @event UploadFile
  729. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  730. * @param {plupload.File} file File to be uploaded.
  731. */
  732. /**
  733. * Fires while a file is being uploaded. Use this event to update the current file upload progress.
  734. *
  735. * @event UploadProgress
  736. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  737. * @param {plupload.File} file File that is currently being uploaded.
  738. */
  739. /**
  740. * Fires when file chunk is uploaded.
  741. *
  742. * @event ChunkUploaded
  743. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  744. * @param {plupload.File} file File that the chunk was uploaded for.
  745. * @param {Object} response Object with response properties.
  746. */
  747. /**
  748. * Fires when a file is successfully uploaded.
  749. *
  750. * @event FileUploaded
  751. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  752. * @param {plupload.File} file File that was uploaded.
  753. * @param {Object} response Object with response properties.
  754. */
  755. /**
  756. * Fires when all files in a queue are uploaded.
  757. *
  758. * @event UploadComplete
  759. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  760. * @param {Array} files Array of file objects that was added to queue/selected by the user.
  761. */
  762. /**
  763. * Fires when a error occurs.
  764. *
  765. * @event Error
  766. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  767. * @param {Object} error Contains code, message and sometimes file and other details.
  768. */
  769. /**
  770. * Fires when destroy method is called.
  771. *
  772. * @event Destroy
  773. * @param {plupload.Uploader} uploader Uploader instance sending the event.
  774. */
  775. var uid = plupload.guid()
  776. , settings
  777. , files = []
  778. , preferred_caps = {}
  779. , fileInputs = []
  780. , fileDrops = []
  781. , startTime
  782. , total
  783. , disabled = false
  784. , xhr
  785. ;
  786. // Private methods
  787. function uploadNext() {
  788. var file, count = 0, i;
  789. if (this.state == plupload.STARTED) {
  790. // Find first QUEUED file
  791. for (i = 0; i < files.length; i++) {
  792. if (!file && files[i].status == plupload.QUEUED) {
  793. file = files[i];
  794. if (this.trigger("BeforeUpload", file)) {
  795. file.status = plupload.UPLOADING;
  796. this.trigger("UploadFile", file);
  797. }
  798. } else {
  799. count++;
  800. }
  801. }
  802. // All files are DONE or FAILED
  803. if (count == files.length) {
  804. if (this.state !== plupload.STOPPED) {
  805. this.state = plupload.STOPPED;
  806. this.trigger("StateChanged");
  807. }
  808. this.trigger("UploadComplete", files);
  809. }
  810. }
  811. }
  812. function calcFile(file) {
  813. file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
  814. calc();
  815. }
  816. function calc() {
  817. var i, file;
  818. // Reset stats
  819. total.reset();
  820. // Check status, size, loaded etc on all files
  821. for (i = 0; i < files.length; i++) {
  822. file = files[i];
  823. if (file.size !== undef) {
  824. // We calculate totals based on original file size
  825. total.size += file.origSize;
  826. // Since we cannot predict file size after resize, we do opposite and
  827. // interpolate loaded amount to match magnitude of total
  828. total.loaded += file.loaded * file.origSize / file.size;
  829. } else {
  830. total.size = undef;
  831. }
  832. if (file.status == plupload.DONE) {
  833. total.uploaded++;
  834. } else if (file.status == plupload.FAILED) {
  835. total.failed++;
  836. } else {
  837. total.queued++;
  838. }
  839. }
  840. // If we couldn't calculate a total file size then use the number of files to calc percent
  841. if (total.size === undef) {
  842. total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
  843. } else {
  844. total.bytesPerSec = Math.ceil(total.loaded / ((+new Date() - startTime || 1) / 1000.0));
  845. total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
  846. }
  847. }
  848. function getRUID() {
  849. var ctrl = fileInputs[0] || fileDrops[0];
  850. if (ctrl) {
  851. return ctrl.getRuntime().uid;
  852. }
  853. return false;
  854. }
  855. function runtimeCan(file, cap) {
  856. if (file.ruid) {
  857. var info = o.Runtime.getInfo(file.ruid);
  858. if (info) {
  859. return info.can(cap);
  860. }
  861. }
  862. return false;
  863. }
  864. function bindEventListeners() {
  865. this.bind('FilesAdded FilesRemoved', function(up) {
  866. up.trigger('QueueChanged');
  867. up.refresh();
  868. });
  869. this.bind('CancelUpload', onCancelUpload);
  870. this.bind('BeforeUpload', onBeforeUpload);
  871. this.bind('UploadFile', onUploadFile);
  872. this.bind('UploadProgress', onUploadProgress);
  873. this.bind('StateChanged', onStateChanged);
  874. this.bind('QueueChanged', calc);
  875. this.bind('Error', onError);
  876. this.bind('FileUploaded', onFileUploaded);
  877. this.bind('Destroy', onDestroy);
  878. }
  879. function initControls(settings, cb) {
  880. var self = this, inited = 0, queue = [];
  881. // common settings
  882. var options = {
  883. runtime_order: settings.runtimes,
  884. required_caps: settings.required_features,
  885. preferred_caps: preferred_caps,
  886. swf_url: settings.flash_swf_url,
  887. xap_url: settings.silverlight_xap_url
  888. };
  889. // add runtime specific options if any
  890. plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
  891. if (settings[runtime]) {
  892. options[runtime] = settings[runtime];
  893. }
  894. });
  895. // initialize file pickers - there can be many
  896. if (settings.browse_button) {
  897. plupload.each(settings.browse_button, function(el) {
  898. queue.push(function(cb) {
  899. var fileInput = new o.FileInput(plupload.extend({}, options, {
  900. accept: settings.filters.mime_types,
  901. name: settings.file_data_name,
  902. multiple: settings.multi_selection,
  903. container: settings.container,
  904. browse_button: el
  905. }));
  906. fileInput.onready = function() {
  907. var info = o.Runtime.getInfo(this.ruid);
  908. // for backward compatibility
  909. o.extend(self.features, {
  910. chunks: info.can('slice_blob'),
  911. multipart: info.can('send_multipart'),
  912. multi_selection: info.can('select_multiple')
  913. });
  914. inited++;
  915. fileInputs.push(this);
  916. cb();
  917. };
  918. fileInput.onchange = function() {
  919. self.addFile(this.files);
  920. };
  921. fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
  922. if (!disabled) {
  923. if (settings.browse_button_hover) {
  924. if ('mouseenter' === e.type) {
  925. o.addClass(el, settings.browse_button_hover);
  926. } else if ('mouseleave' === e.type) {
  927. o.removeClass(el, settings.browse_button_hover);
  928. }
  929. }
  930. if (settings.browse_button_active) {
  931. if ('mousedown' === e.type) {
  932. o.addClass(el, settings.browse_button_active);
  933. } else if ('mouseup' === e.type) {
  934. o.removeClass(el, settings.browse_button_active);
  935. }
  936. }
  937. }
  938. });
  939. fileInput.bind('mousedown', function() {
  940. self.trigger('Browse');
  941. });
  942. fileInput.bind('error runtimeerror', function() {
  943. fileInput = null;
  944. cb();
  945. });
  946. fileInput.init();
  947. });
  948. });
  949. }
  950. // initialize drop zones
  951. if (settings.drop_element) {
  952. plupload.each(settings.drop_element, function(el) {
  953. queue.push(function(cb) {
  954. var fileDrop = new o.FileDrop(plupload.extend({}, options, {
  955. drop_zone: el
  956. }));
  957. fileDrop.onready = function() {
  958. var info = o.Runtime.getInfo(this.ruid);
  959. self.features.dragdrop = info.can('drag_and_drop'); // for backward compatibility
  960. inited++;
  961. fileDrops.push(this);
  962. cb();
  963. };
  964. fileDrop.ondrop = function() {
  965. self.addFile(this.files);
  966. };
  967. fileDrop.bind('error runtimeerror', function() {
  968. fileDrop = null;
  969. cb();
  970. });
  971. fileDrop.init();
  972. });
  973. });
  974. }
  975. o.inSeries(queue, function() {
  976. if (typeof(cb) === 'function') {
  977. cb(inited);
  978. }
  979. });
  980. }
  981. function resizeImage(blob, params, cb) {
  982. var img = new o.Image();
  983. try {
  984. img.onload = function() {
  985. // no manipulation required if...
  986. if (params.width > this.width &&
  987. params.height > this.height &&
  988. params.quality === undef &&
  989. params.preserve_headers &&
  990. !params.crop
  991. ) {
  992. this.destroy();
  993. return cb(blob);
  994. }
  995. // otherwise downsize
  996. img.downsize(params.width, params.height, params.crop, params.preserve_headers);
  997. };
  998. img.onresize = function() {
  999. cb(this.getAsBlob(blob.type, params.quality));
  1000. this.destroy();
  1001. };
  1002. img.onerror = function() {
  1003. cb(blob);
  1004. };
  1005. img.load(blob);
  1006. } catch(ex) {
  1007. cb(blob);
  1008. }
  1009. }
  1010. function setOption(option, value, init) {
  1011. var self = this, reinitRequired = false;
  1012. function _setOption(option, value, init) {
  1013. var oldValue = settings[option];
  1014. switch (option) {
  1015. case 'max_file_size':
  1016. if (option === 'max_file_size') {
  1017. settings.max_file_size = settings.filters.max_file_size = value;
  1018. }
  1019. break;
  1020. case 'chunk_size':
  1021. if (value = plupload.parseSize(value)) {
  1022. settings[option] = value;
  1023. settings.send_file_name = true;
  1024. }
  1025. break;
  1026. case 'multipart':
  1027. settings[option] = value;
  1028. if (!value) {
  1029. settings.send_file_name = true;
  1030. }
  1031. break;
  1032. case 'unique_names':
  1033. settings[option] = value;
  1034. if (value) {
  1035. settings.send_file_name = true;
  1036. }
  1037. break;
  1038. case 'filters':
  1039. // for sake of backward compatibility
  1040. if (plupload.typeOf(value) === 'array') {
  1041. value = {
  1042. mime_types: value
  1043. };
  1044. }
  1045. if (init) {
  1046. plupload.extend(settings.filters, value);
  1047. } else {
  1048. settings.filters = value;
  1049. }
  1050. // if file format filters are being updated, regenerate the matching expressions
  1051. if (value.mime_types) {
  1052. settings.filters.mime_types.regexp = (function(filters) {
  1053. var extensionsRegExp = [];
  1054. plupload.each(filters, function(filter) {
  1055. plupload.each(filter.extensions.split(/,/), function(ext) {
  1056. if (/^\s*\*\s*$/.test(ext)) {
  1057. extensionsRegExp.push('\\.*');
  1058. } else {
  1059. extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
  1060. }
  1061. });
  1062. });
  1063. return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
  1064. }(settings.filters.mime_types));
  1065. }
  1066. break;
  1067. case 'resize':
  1068. if (init) {
  1069. plupload.extend(settings.resize, value, {
  1070. enabled: true
  1071. });
  1072. } else {
  1073. settings.resize = value;
  1074. }
  1075. break;
  1076. case 'prevent_duplicates':
  1077. settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
  1078. break;
  1079. case 'browse_button':
  1080. case 'drop_element':
  1081. value = plupload.get(value);
  1082. case 'container':
  1083. case 'runtimes':
  1084. case 'multi_selection':
  1085. case 'flash_swf_url':
  1086. case 'silverlight_xap_url':
  1087. settings[option] = value;
  1088. if (!init) {
  1089. reinitRequired = true;
  1090. }
  1091. break;
  1092. default:
  1093. settings[option] = value;
  1094. }
  1095. if (!init) {
  1096. self.trigger('OptionChanged', option, value, oldValue);
  1097. }
  1098. }
  1099. if (typeof(option) === 'object') {
  1100. plupload.each(option, function(value, option) {
  1101. _setOption(option, value, init);
  1102. });
  1103. } else {
  1104. _setOption(option, value, init);
  1105. }
  1106. if (init) {
  1107. // Normalize the list of required capabilities
  1108. settings.required_features = normalizeCaps(plupload.extend({}, settings));
  1109. // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
  1110. preferred_caps = normalizeCaps(plupload.extend({}, settings, {
  1111. required_features: true
  1112. }));
  1113. } else if (reinitRequired) {
  1114. self.trigger('Destroy');
  1115. initControls.call(self, settings, function(inited) {
  1116. if (inited) {
  1117. self.runtime = o.Runtime.getInfo(getRUID()).type;
  1118. self.trigger('Init', { runtime: self.runtime });
  1119. self.trigger('PostInit');
  1120. } else {
  1121. self.trigger('Error', {
  1122. code : plupload.INIT_ERROR,
  1123. message : plupload.translate('Init error.')
  1124. });
  1125. }
  1126. });
  1127. }
  1128. }
  1129. // Internal event handlers
  1130. function onBeforeUpload(up, file) {
  1131. // Generate unique target filenames
  1132. if (up.settings.unique_names) {
  1133. var matches = file.name.match(/\.([^.]+)$/), ext = "part";
  1134. if (matches) {
  1135. ext = matches[1];
  1136. }
  1137. file.target_name = file.id + '.' + ext;
  1138. }
  1139. }
  1140. function onUploadFile(up, file) {
  1141. var url = up.settings.url
  1142. , chunkSize = up.settings.chunk_size
  1143. , retries = up.settings.max_retries
  1144. , features = up.features
  1145. , offset = 0
  1146. , blob
  1147. ;
  1148. // make sure we start at a predictable offset
  1149. if (file.loaded) {
  1150. offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
  1151. }
  1152. function handleError() {
  1153. if (retries-- > 0) {
  1154. delay(uploadNextChunk, 1000);
  1155. } else {
  1156. file.loaded = offset; // reset all progress
  1157. up.trigger('Error', {
  1158. code : plupload.HTTP_ERROR,
  1159. message : plupload.translate('HTTP Error.'),
  1160. file : file,
  1161. response : xhr.responseText,
  1162. status : xhr.status,
  1163. responseHeaders: xhr.getAllResponseHeaders()
  1164. });
  1165. }
  1166. }
  1167. function uploadNextChunk() {
  1168. var chunkBlob, formData, args = {}, curChunkSize;
  1169. // make sure that file wasn't cancelled and upload is not stopped in general
  1170. if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
  1171. return;
  1172. }
  1173. // send additional 'name' parameter only if required
  1174. if (up.settings.send_file_name) {
  1175. args.name = file.target_name || file.name;
  1176. }
  1177. if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
  1178. curChunkSize = Math.min(chunkSize, blob.size - offset);
  1179. chunkBlob = blob.slice(offset, offset + curChunkSize);
  1180. } else {
  1181. curChunkSize = blob.size;
  1182. chunkBlob = blob;
  1183. }
  1184. // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
  1185. if (chunkSize && features.chunks) {
  1186. // Setup query string arguments
  1187. if (up.settings.send_chunk_number) {
  1188. args.chunk = Math.ceil(offset / chunkSize);
  1189. args.chunks = Math.ceil(blob.size / chunkSize);
  1190. } else { // keep support for experimental chunk format, just in case
  1191. args.offset = offset;
  1192. args.total = blob.size;
  1193. }
  1194. }
  1195. xhr = new o.XMLHttpRequest();
  1196. // Do we have upload progress support
  1197. if (xhr.upload) {
  1198. xhr.upload.onprogress = function(e) {
  1199. file.loaded = Math.min(file.size, offset + e.loaded);
  1200. up.trigger('UploadProgress', file);
  1201. };
  1202. }
  1203. xhr.onload = function() {
  1204. // check if upload made itself through
  1205. if (xhr.status >= 400) {
  1206. handleError();
  1207. return;
  1208. }
  1209. retries = up.settings.max_retries; // reset the counter
  1210. // Handle chunk response
  1211. if (curChunkSize < blob.size) {
  1212. chunkBlob.destroy();
  1213. offset += curChunkSize;
  1214. file.loaded = Math.min(offset, blob.size);
  1215. up.trigger('ChunkUploaded', file, {
  1216. offset : file.loaded,
  1217. total : blob.size,
  1218. response : xhr.responseText,
  1219. status : xhr.status,
  1220. responseHeaders: xhr.getAllResponseHeaders()
  1221. });
  1222. // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
  1223. if (o.Env.browser === 'Android Browser') {
  1224. // doesn't harm in general, but is not required anywhere else
  1225. up.trigger('UploadProgress', file);
  1226. }
  1227. } else {
  1228. file.loaded = file.size;
  1229. }
  1230. chunkBlob = formData = null; // Free memory
  1231. // Check if file is uploaded
  1232. if (!offset || offset >= blob.size) {
  1233. // If file was modified, destory the copy
  1234. if (file.size != file.origSize) {
  1235. blob.destroy();
  1236. blob = null;
  1237. }
  1238. up.trigger('UploadProgress', file);
  1239. file.status = plupload.DONE;
  1240. up.trigger('FileUploaded', file, {
  1241. response : xhr.responseText,
  1242. status : xhr.status,
  1243. responseHeaders: xhr.getAllResponseHeaders()
  1244. });
  1245. } else {
  1246. // Still chunks left
  1247. delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
  1248. }
  1249. };
  1250. xhr.onerror = function() {
  1251. handleError();
  1252. };
  1253. xhr.onloadend = function() {
  1254. this.destroy();
  1255. xhr = null;
  1256. };
  1257. // Build multipart request
  1258. if (up.settings.multipart && features.multipart) {
  1259. xhr.open("post", url, true);
  1260. // Set custom headers
  1261. plupload.each(up.settings.headers, function(value, name) {
  1262. xhr.setRequestHeader(name, value);
  1263. });
  1264. formData = new o.FormData();
  1265. // Add multipart params
  1266. plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
  1267. formData.append(name, value);
  1268. });
  1269. // Add file and send it
  1270. formData.append(up.settings.file_data_name, chunkBlob);
  1271. xhr.send(formData, {
  1272. runtime_order: up.settings.runtimes,
  1273. required_caps: up.settings.required_features,
  1274. preferred_caps: preferred_caps,
  1275. swf_url: up.settings.flash_swf_url,
  1276. xap_url: up.settings.silverlight_xap_url
  1277. });
  1278. } else {
  1279. // if no multipart, send as binary stream
  1280. url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
  1281. xhr.open("post", url, true);
  1282. xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
  1283. // Set custom headers
  1284. plupload.each(up.settings.headers, function(value, name) {
  1285. xhr.setRequestHeader(name, value);
  1286. });
  1287. xhr.send(chunkBlob, {
  1288. runtime_order: up.settings.runtimes,
  1289. required_caps: up.settings.required_features,
  1290. preferred_caps: preferred_caps,
  1291. swf_url: up.settings.flash_swf_url,
  1292. xap_url: up.settings.silverlight_xap_url
  1293. });
  1294. }
  1295. }
  1296. blob = file.getSource();
  1297. // Start uploading chunks
  1298. if (up.settings.resize.enabled && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) {
  1299. // Resize if required
  1300. resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
  1301. blob = resizedBlob;
  1302. file.size = resizedBlob.size;
  1303. uploadNextChunk();
  1304. });
  1305. } else {
  1306. uploadNextChunk();
  1307. }
  1308. }
  1309. function onUploadProgress(up, file) {
  1310. calcFile(file);
  1311. }
  1312. function onStateChanged(up) {
  1313. if (up.state == plupload.STARTED) {
  1314. // Get start time to calculate bps
  1315. startTime = (+new Date());
  1316. } else if (up.state == plupload.STOPPED) {
  1317. // Reset currently uploading files
  1318. for (var i = up.files.length - 1; i >= 0; i--) {
  1319. if (up.files[i].status == plupload.UPLOADING) {
  1320. up.files[i].status = plupload.QUEUED;
  1321. calc();
  1322. }
  1323. }
  1324. }
  1325. }
  1326. function onCancelUpload() {
  1327. if (xhr) {
  1328. xhr.abort();
  1329. }
  1330. }
  1331. function onFileUploaded(up) {
  1332. calc();
  1333. // Upload next file but detach it from the error event
  1334. // since other custom listeners might want to stop the queue
  1335. delay(function() {
  1336. uploadNext.call(up);
  1337. }, 1);
  1338. }
  1339. function onError(up, err) {
  1340. if (err.code === plupload.INIT_ERROR) {
  1341. up.destroy();
  1342. }
  1343. // Set failed status if an error occured on a file
  1344. else if (err.file) {
  1345. err.file.status = plupload.FAILED;
  1346. calcFile(err.file);
  1347. // Upload next file but detach it from the error event
  1348. // since other custom listeners might want to stop the queue
  1349. if (up.state == plupload.STARTED) { // upload in progress
  1350. up.trigger('CancelUpload');
  1351. delay(function() {
  1352. uploadNext.call(up);
  1353. }, 1);
  1354. }
  1355. }
  1356. }
  1357. function onDestroy(up) {
  1358. up.stop();
  1359. // Purge the queue
  1360. plupload.each(files, function(file) {
  1361. file.destroy();
  1362. });
  1363. files = [];
  1364. if (fileInputs.length) {
  1365. plupload.each(fileInputs, function(fileInput) {
  1366. fileInput.destroy();
  1367. });
  1368. fileInputs = [];
  1369. }
  1370. if (fileDrops.length) {
  1371. plupload.each(fileDrops, function(fileDrop) {
  1372. fileDrop.destroy();
  1373. });
  1374. fileDrops = [];
  1375. }
  1376. preferred_caps = {};
  1377. disabled = false;
  1378. startTime = xhr = null;
  1379. total.reset();
  1380. }
  1381. // Default settings
  1382. settings = {
  1383. runtimes: o.Runtime.order,
  1384. max_retries: 0,
  1385. chunk_size: 0,
  1386. multipart: true,
  1387. multi_selection: true,
  1388. file_data_name: 'file',
  1389. flash_swf_url: 'js/Moxie.swf',
  1390. silverlight_xap_url: 'js/Moxie.xap',
  1391. filters: {
  1392. mime_types: [],
  1393. prevent_duplicates: false,
  1394. max_file_size: 0
  1395. },
  1396. resize: {
  1397. enabled: false,
  1398. preserve_headers: true,
  1399. crop: false
  1400. },
  1401. send_file_name: true,
  1402. send_chunk_number: true
  1403. };
  1404. setOption.call(this, options, null, true);
  1405. // Inital total state
  1406. total = new plupload.QueueProgress();
  1407. // Add public methods
  1408. plupload.extend(this, {
  1409. /**
  1410. * Unique id for the Uploader instance.
  1411. *
  1412. * @property id
  1413. * @type String
  1414. */
  1415. id : uid,
  1416. uid : uid, // mOxie uses this to differentiate between event targets
  1417. /**
  1418. * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
  1419. * These states are controlled by the stop/start methods. The default value is STOPPED.
  1420. *
  1421. * @property state
  1422. * @type Number
  1423. */
  1424. state : plupload.STOPPED,
  1425. /**
  1426. * Map of features that are available for the uploader runtime. Features will be filled
  1427. * before the init event is called, these features can then be used to alter the UI for the end user.
  1428. * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
  1429. *
  1430. * @property features
  1431. * @type Object
  1432. */
  1433. features : {},
  1434. /**
  1435. * Current runtime name.
  1436. *
  1437. * @property runtime
  1438. * @type String
  1439. */
  1440. runtime : null,
  1441. /**
  1442. * Current upload queue, an array of File instances.
  1443. *
  1444. * @property files
  1445. * @type Array
  1446. * @see plupload.File
  1447. */
  1448. files : files,
  1449. /**
  1450. * Object with name/value settings.
  1451. *
  1452. * @property settings
  1453. * @type Object
  1454. */
  1455. settings : settings,
  1456. /**
  1457. * Total progess information. How many files has been uploaded, total percent etc.
  1458. *
  1459. * @property total
  1460. * @type plupload.QueueProgress
  1461. */
  1462. total : total,
  1463. /**
  1464. * Initializes the Uploader instance and adds internal event listeners.
  1465. *
  1466. * @method init
  1467. */
  1468. init : function() {
  1469. var self = this;
  1470. if (typeof(settings.preinit) == "function") {
  1471. settings.preinit(self);
  1472. } else {
  1473. plupload.each(settings.preinit, function(func, name) {
  1474. self.bind(name, func);
  1475. });
  1476. }
  1477. bindEventListeners.call(this);
  1478. // Check for required options
  1479. if (!settings.browse_button || !settings.url) {
  1480. this.trigger('Error', {
  1481. code : plupload.INIT_ERROR,
  1482. message : plupload.translate('Init error.')
  1483. });
  1484. return;
  1485. }
  1486. initControls.call(this, settings, function(inited) {
  1487. if (typeof(settings.init) == "function") {
  1488. settings.init(self);
  1489. } else {
  1490. plupload.each(settings.init, function(func, name) {
  1491. self.bind(name, func);
  1492. });
  1493. }
  1494. if (inited) {
  1495. self.runtime = o.Runtime.getInfo(getRUID()).type;
  1496. self.trigger('Init', { runtime: self.runtime });
  1497. self.trigger('PostInit');
  1498. } else {
  1499. self.trigger('Error', {
  1500. code : plupload.INIT_ERROR,
  1501. message : plupload.translate('Init error.')
  1502. });
  1503. }
  1504. });
  1505. },
  1506. /**
  1507. * Set the value for the specified option(s).
  1508. *
  1509. * @method setOption
  1510. * @since 2.1
  1511. * @param {String|Object} option Name of the option to change or the set of key/value pairs
  1512. * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
  1513. */
  1514. setOption: function(option, value) {
  1515. setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
  1516. },
  1517. /**
  1518. * Get the value for the specified option or the whole configuration, if not specified.
  1519. *
  1520. * @method getOption
  1521. * @since 2.1
  1522. * @param {String} [option] Name of the option to get
  1523. * @return {Mixed} Value for the option or the whole set
  1524. */
  1525. getOption: function(option) {
  1526. if (!option) {
  1527. return settings;
  1528. }
  1529. return settings[option];
  1530. },
  1531. /**
  1532. * Refreshes the upload instance by dispatching out a refresh event to all runtimes.
  1533. * This would for example reposition flash/silverlight shims on the page.
  1534. *
  1535. * @method refresh
  1536. */
  1537. refresh : function() {
  1538. if (fileInputs.length) {
  1539. plupload.each(fileInputs, function(fileInput) {
  1540. fileInput.trigger('Refresh');
  1541. });
  1542. }
  1543. this.trigger('Refresh');
  1544. },
  1545. /**
  1546. * Starts uploading the queued files.
  1547. *
  1548. * @method start
  1549. */
  1550. start : function() {
  1551. if (this.state != plupload.STARTED) {
  1552. this.state = plupload.STARTED;
  1553. this.trigger('StateChanged');
  1554. uploadNext.call(this);
  1555. }
  1556. },
  1557. /**
  1558. * Stops the upload of the queued files.
  1559. *
  1560. * @method stop
  1561. */
  1562. stop : function() {
  1563. if (this.state != plupload.STOPPED) {
  1564. this.state = plupload.STOPPED;
  1565. this.trigger('StateChanged');
  1566. this.trigger('CancelUpload');
  1567. }
  1568. },
  1569. /**
  1570. * Disables/enables browse button on request.
  1571. *
  1572. * @method disableBrowse
  1573. * @param {Boolean} disable Whether to disable or enable (default: true)
  1574. */
  1575. disableBrowse : function() {
  1576. disabled = arguments[0] !== undef ? arguments[0] : true;
  1577. if (fileInputs.length) {
  1578. plupload.each(fileInputs, function(fileInput) {
  1579. fileInput.disable(disabled);
  1580. });
  1581. }
  1582. this.trigger('DisableBrowse', disabled);
  1583. },
  1584. /**
  1585. * Returns the specified file object by id.
  1586. *
  1587. * @method getFile
  1588. * @param {String} id File id to look for.
  1589. * @return {plupload.File} File object or undefined if it wasn't found;
  1590. */
  1591. getFile : function(id) {
  1592. var i;
  1593. for (i = files.length - 1; i >= 0; i--) {
  1594. if (files[i].id === id) {
  1595. return files[i];
  1596. }
  1597. }
  1598. },
  1599. /**
  1600. * Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
  1601. * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,
  1602. * if any files were added to the queue. Otherwise nothing happens.
  1603. *
  1604. * @method addFile
  1605. * @since 2.0
  1606. * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
  1607. * @param {String} [fileName] If specified, will be used as a name for the file
  1608. */
  1609. addFile : function(file, fileName) {
  1610. var self = this
  1611. , queue = []
  1612. , filesAdded = []
  1613. , ruid
  1614. ;
  1615. function filterFile(file, cb) {
  1616. var queue = [];
  1617. o.each(self.settings.filters, function(rule, name) {
  1618. if (fileFilters[name]) {
  1619. queue.push(function(cb) {
  1620. fileFilters[name].call(self, rule, file, function(res) {
  1621. cb(!res);
  1622. });
  1623. });
  1624. }
  1625. });
  1626. o.inSeries(queue, cb);
  1627. }
  1628. /**
  1629. * @method resolveFile
  1630. * @private
  1631. * @param {o.File|o.Blob|plupload.File|File|Blob|input[type="file"]} file
  1632. */
  1633. function resolveFile(file) {
  1634. var type = o.typeOf(file);
  1635. // o.File
  1636. if (file instanceof o.File) {
  1637. if (!file.ruid && !file.isDetached()) {
  1638. if (!ruid) { // weird case
  1639. return false;
  1640. }
  1641. file.ruid = ruid;
  1642. file.connectRuntime(ruid);
  1643. }
  1644. resolveFile(new plupload.File(file));
  1645. }
  1646. // o.Blob
  1647. else if (file instanceof o.Blob) {
  1648. resolveFile(file.getSource());
  1649. file.destroy();
  1650. }
  1651. // plupload.File - final step for other branches
  1652. else if (file instanceof plupload.File) {
  1653. if (fileName) {
  1654. file.name = fileName;
  1655. }
  1656. queue.push(function(cb) {
  1657. // run through the internal and user-defined filters, if any
  1658. filterFile(file, function(err) {
  1659. if (!err) {
  1660. // make files available for the filters by updating the main queue directly
  1661. files.push(file);
  1662. // collect the files that will be passed to FilesAdded event
  1663. filesAdded.push(file);
  1664. self.trigger("FileFiltered", file);
  1665. }
  1666. delay(cb, 1); // do not build up recursions or eventually we might hit the limits
  1667. });
  1668. });
  1669. }
  1670. // native File or blob
  1671. else if (o.inArray(type, ['file', 'blob']) !== -1) {
  1672. resolveFile(new o.File(null, file));
  1673. }
  1674. // input[type="file"]
  1675. else if (type === 'node' && o.typeOf(file.files) === 'filelist') {
  1676. // if we are dealing with input[type="file"]
  1677. o.each(file.files, resolveFile);
  1678. }
  1679. // mixed array of any supported types (see above)
  1680. else if (type === 'array') {
  1681. fileName = null; // should never happen, but unset anyway to avoid funny situations
  1682. o.each(file, resolveFile);
  1683. }
  1684. }
  1685. ruid = getRUID();
  1686. resolveFile(file);
  1687. if (queue.length) {
  1688. o.inSeries(queue, function() {
  1689. // if any files left after filtration, trigger FilesAdded
  1690. if (filesAdded.length) {
  1691. self.trigger("FilesAdded", filesAdded);
  1692. }
  1693. });
  1694. }
  1695. },
  1696. /**
  1697. * Removes a specific file.
  1698. *
  1699. * @method removeFile
  1700. * @param {plupload.File|String} file File to remove from queue.
  1701. */
  1702. removeFile : function(file) {
  1703. var id = typeof(file) === 'string' ? file : file.id;
  1704. for (var i = files.length - 1; i >= 0; i--) {
  1705. if (files[i].id === id) {
  1706. return this.splice(i, 1)[0];
  1707. }
  1708. }
  1709. },
  1710. /**
  1711. * Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events.
  1712. *
  1713. * @method splice
  1714. * @param {Number} start (Optional) Start index to remove from.
  1715. * @param {Number} length (Optional) Lengh of items to remove.
  1716. * @return {Array} Array of files that was removed.
  1717. */
  1718. splice : function(start, length) {
  1719. // Splice and trigger events
  1720. var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
  1721. // if upload is in progress we need to stop it and restart after files are removed
  1722. var restartRequired = false;
  1723. if (this.state == plupload.STARTED) { // upload in progress
  1724. plupload.each(removed, function(file) {
  1725. if (file.status === plupload.UPLOADING) {
  1726. restartRequired = true; // do not restart, unless file that is being removed is uploading
  1727. return false;
  1728. }
  1729. });
  1730. if (restartRequired) {
  1731. this.stop();
  1732. }
  1733. }
  1734. this.trigger("FilesRemoved", removed);
  1735. // Dispose any resources allocated by those files
  1736. plupload.each(removed, function(file) {
  1737. file.destroy();
  1738. });
  1739. if (restartRequired) {
  1740. this.start();
  1741. }
  1742. return removed;
  1743. },
  1744. /**
  1745. * Dispatches the specified event name and it's arguments to all listeners.
  1746. *
  1747. *
  1748. * @method trigger
  1749. * @param {String} name Event name to fire.
  1750. * @param {Object..} Multiple arguments to pass along to the listener functions.
  1751. */
  1752. /**
  1753. * Check whether uploader has any listeners to the specified event.
  1754. *
  1755. * @method hasEventListener
  1756. * @param {String} name Event name to check for.
  1757. */
  1758. /**
  1759. * Adds an event listener by name.
  1760. *
  1761. * @method bind
  1762. * @param {String} name Event name to listen for.
  1763. * @param {function} func Function to call ones the event gets fired.
  1764. * @param {Object} scope Optional scope to execute the specified function in.
  1765. */
  1766. bind : function(name, func, scope) {
  1767. var self = this;
  1768. // adapt moxie EventTarget style to Plupload-like
  1769. plupload.Uploader.prototype.bind.call(this, name, function() {
  1770. var args = [].slice.call(arguments);
  1771. args.splice(0, 1, self); // replace event object with uploader instance
  1772. return func.apply(this, args);
  1773. }, 0, scope);
  1774. },
  1775. /**
  1776. * Removes the specified event listener.
  1777. *
  1778. * @method unbind
  1779. * @param {String} name Name of event to remove.
  1780. * @param {function} func Function to remove from listener.
  1781. */
  1782. /**
  1783. * Removes all event listeners.
  1784. *
  1785. * @method unbindAll
  1786. */
  1787. /**
  1788. * Destroys Plupload instance and cleans after itself.
  1789. *
  1790. * @method destroy
  1791. */
  1792. destroy : function() {
  1793. this.trigger('Destroy');
  1794. settings = total = null; // purge these exclusively
  1795. this.unbindAll();
  1796. }
  1797. });
  1798. };
  1799. plupload.Uploader.prototype = o.EventTarget.instance;
  1800. /**
  1801. * Constructs a new file instance.
  1802. *
  1803. * @class File
  1804. * @constructor
  1805. *
  1806. * @param {Object} file Object containing file properties
  1807. * @param {String} file.name Name of the file.
  1808. * @param {Number} file.size File size.
  1809. */
  1810. plupload.File = (function() {
  1811. var filepool = {};
  1812. function PluploadFile(file) {
  1813. plupload.extend(this, {
  1814. /**
  1815. * File id this is a globally unique id for the specific file.
  1816. *
  1817. * @property id
  1818. * @type String
  1819. */
  1820. id: plupload.guid(),
  1821. /**
  1822. * File name for example "myfile.gif".
  1823. *
  1824. * @property name
  1825. * @type String
  1826. */
  1827. name: file.name || file.fileName,
  1828. /**
  1829. * File type, `e.g image/jpeg`
  1830. *
  1831. * @property type
  1832. * @type String
  1833. */
  1834. type: file.type || '',
  1835. /**
  1836. * File size in bytes (may change after client-side manupilation).
  1837. *
  1838. * @property size
  1839. * @type Number
  1840. */
  1841. size: file.size || file.fileSize,
  1842. /**
  1843. * Original file size in bytes.
  1844. *
  1845. * @property origSize
  1846. * @type Number
  1847. */
  1848. origSize: file.size || file.fileSize,
  1849. /**
  1850. * Number of bytes uploaded of the files total size.
  1851. *
  1852. * @property loaded
  1853. * @type Number
  1854. */
  1855. loaded: 0,
  1856. /**
  1857. * Number of percentage uploaded of the file.
  1858. *
  1859. * @property percent
  1860. * @type Number
  1861. */
  1862. percent: 0,
  1863. /**
  1864. * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
  1865. *
  1866. * @property status
  1867. * @type Number
  1868. * @see plupload
  1869. */
  1870. status: plupload.QUEUED,
  1871. /**
  1872. * Date of last modification.
  1873. *
  1874. * @property lastModifiedDate
  1875. * @type {String}
  1876. */
  1877. lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
  1878. /**
  1879. * Returns native window.File object, when it's available.
  1880. *
  1881. * @method getNative
  1882. * @return {window.File} or null, if plupload.File is of different origin
  1883. */
  1884. getNative: function() {
  1885. var file = this.getSource().getSource();
  1886. return o.inArray(o.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
  1887. },
  1888. /**
  1889. * Returns mOxie.File - unified wrapper object that can be used across runtimes.
  1890. *
  1891. * @method getSource
  1892. * @return {mOxie.File} or null
  1893. */
  1894. getSource: function() {
  1895. if (!filepool[this.id]) {
  1896. return null;
  1897. }
  1898. return filepool[this.id];
  1899. },
  1900. /**
  1901. * Destroys plupload.File object.
  1902. *
  1903. * @method destroy
  1904. */
  1905. destroy: function() {
  1906. var src = this.getSource();
  1907. if (src) {
  1908. src.destroy();
  1909. delete filepool[this.id];
  1910. }
  1911. }
  1912. });
  1913. filepool[this.id] = file;
  1914. }
  1915. return PluploadFile;
  1916. }());
  1917. /**
  1918. * Constructs a queue progress.
  1919. *
  1920. * @class QueueProgress
  1921. * @constructor
  1922. */
  1923. plupload.QueueProgress = function() {
  1924. var self = this; // Setup alias for self to reduce code size when it's compressed
  1925. /**
  1926. * Total queue file size.
  1927. *
  1928. * @property size
  1929. * @type Number
  1930. */
  1931. self.size = 0;
  1932. /**
  1933. * Total bytes uploaded.
  1934. *
  1935. * @property loaded
  1936. * @type Number
  1937. */
  1938. self.loaded = 0;
  1939. /**
  1940. * Number of files uploaded.
  1941. *
  1942. * @property uploaded
  1943. * @type Number
  1944. */
  1945. self.uploaded = 0;
  1946. /**
  1947. * Number of files failed to upload.
  1948. *
  1949. * @property failed
  1950. * @type Number
  1951. */
  1952. self.failed = 0;
  1953. /**
  1954. * Number of files yet to be uploaded.
  1955. *
  1956. * @property queued
  1957. * @type Number
  1958. */
  1959. self.queued = 0;
  1960. /**
  1961. * Total percent of the uploaded bytes.
  1962. *
  1963. * @property percent
  1964. * @type Number
  1965. */
  1966. self.percent = 0;
  1967. /**
  1968. * Bytes uploaded per second.
  1969. *
  1970. * @property bytesPerSec
  1971. * @type Number
  1972. */
  1973. self.bytesPerSec = 0;
  1974. /**
  1975. * Resets the progress to it's initial values.
  1976. *
  1977. * @method reset
  1978. */
  1979. self.reset = function() {
  1980. self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
  1981. };
  1982. };
  1983. window.plupload = plupload;
  1984. }(window, mOxie));