FileAPI.js 102 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313
  1. /*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git
  2. * FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.
  3. */
  4. /*
  5. * JavaScript Canvas to Blob 2.0.5
  6. * https://github.com/blueimp/JavaScript-Canvas-to-Blob
  7. *
  8. * Copyright 2012, Sebastian Tschan
  9. * https://blueimp.net
  10. *
  11. * Licensed under the MIT license:
  12. * http://www.opensource.org/licenses/MIT
  13. *
  14. * Based on stackoverflow user Stoive's code snippet:
  15. * http://stackoverflow.com/q/4998908
  16. */
  17. /*jslint nomen: true, regexp: true */
  18. /*global window, atob, Blob, ArrayBuffer, Uint8Array */
  19. (function (window) {
  20. 'use strict';
  21. var CanvasPrototype = window.HTMLCanvasElement &&
  22. window.HTMLCanvasElement.prototype,
  23. hasBlobConstructor = window.Blob && (function () {
  24. try {
  25. return Boolean(new Blob());
  26. } catch (e) {
  27. return false;
  28. }
  29. }()),
  30. hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
  31. (function () {
  32. try {
  33. return new Blob([new Uint8Array(100)]).size === 100;
  34. } catch (e) {
  35. return false;
  36. }
  37. }()),
  38. BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
  39. window.MozBlobBuilder || window.MSBlobBuilder,
  40. dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
  41. window.ArrayBuffer && window.Uint8Array && function (dataURI) {
  42. var byteString,
  43. arrayBuffer,
  44. intArray,
  45. i,
  46. mimeString,
  47. bb;
  48. if (dataURI.split(',')[0].indexOf('base64') >= 0) {
  49. // Convert base64 to raw binary data held in a string:
  50. byteString = atob(dataURI.split(',')[1]);
  51. } else {
  52. // Convert base64/URLEncoded data component to raw binary data:
  53. byteString = decodeURIComponent(dataURI.split(',')[1]);
  54. }
  55. // Write the bytes of the string to an ArrayBuffer:
  56. arrayBuffer = new ArrayBuffer(byteString.length);
  57. intArray = new Uint8Array(arrayBuffer);
  58. for (i = 0; i < byteString.length; i += 1) {
  59. intArray[i] = byteString.charCodeAt(i);
  60. }
  61. // Separate out the mime component:
  62. mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
  63. // Write the ArrayBuffer (or ArrayBufferView) to a blob:
  64. if (hasBlobConstructor) {
  65. return new Blob(
  66. [hasArrayBufferViewSupport ? intArray : arrayBuffer],
  67. {type: mimeString}
  68. );
  69. }
  70. bb = new BlobBuilder();
  71. bb.append(arrayBuffer);
  72. return bb.getBlob(mimeString);
  73. };
  74. if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
  75. if (CanvasPrototype.mozGetAsFile) {
  76. CanvasPrototype.toBlob = function (callback, type, quality) {
  77. if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
  78. callback(dataURLtoBlob(this.toDataURL(type, quality)));
  79. } else {
  80. callback(this.mozGetAsFile('blob', type));
  81. }
  82. };
  83. } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
  84. CanvasPrototype.toBlob = function (callback, type, quality) {
  85. callback(dataURLtoBlob(this.toDataURL(type, quality)));
  86. };
  87. }
  88. }
  89. window.dataURLtoBlob = dataURLtoBlob;
  90. })(window);
  91. /*jslint evil: true */
  92. /*global window, URL, webkitURL, ActiveXObject */
  93. (function (window, undef){
  94. 'use strict';
  95. var
  96. gid = 1,
  97. noop = function (){},
  98. document = window.document,
  99. doctype = document.doctype || {},
  100. userAgent = window.navigator.userAgent,
  101. // https://github.com/blueimp/JavaScript-Load-Image/blob/master/load-image.js#L48
  102. apiURL = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL),
  103. Blob = window.Blob,
  104. File = window.File,
  105. FileReader = window.FileReader,
  106. FormData = window.FormData,
  107. XMLHttpRequest = window.XMLHttpRequest,
  108. jQuery = window.jQuery,
  109. html5 = !!(File && (FileReader && (window.Uint8Array || FormData || XMLHttpRequest.prototype.sendAsBinary)))
  110. && !(/safari\//i.test(userAgent) && !/chrome\//i.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25
  111. cors = html5 && ('withCredentials' in (new XMLHttpRequest)),
  112. chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice || Blob.prototype.mozSlice || Blob.prototype.slice),
  113. // https://github.com/blueimp/JavaScript-Canvas-to-Blob
  114. dataURLtoBlob = window.dataURLtoBlob,
  115. _rimg = /img/i,
  116. _rcanvas = /canvas/i,
  117. _rimgcanvas = /img|canvas/i,
  118. _rinput = /input/i,
  119. _rdata = /^data:[^,]+,/,
  120. _toString = {}.toString,
  121. Math = window.Math,
  122. _SIZE_CONST = function (pow){
  123. pow = new window.Number(Math.pow(1024, pow));
  124. pow.from = function (sz){ return Math.round(sz * this); };
  125. return pow;
  126. },
  127. _elEvents = {}, // element event listeners
  128. _infoReader = [], // list of file info processors
  129. _readerEvents = 'abort progress error load loadend',
  130. _xhrPropsExport = 'status statusText readyState response responseXML responseText responseBody'.split(' '),
  131. currentTarget = 'currentTarget', // for minimize
  132. preventDefault = 'preventDefault', // and this too
  133. _isArray = function (ar) {
  134. return ar && ('length' in ar);
  135. },
  136. /**
  137. * Iterate over a object or array
  138. */
  139. _each = function (obj, fn, ctx){
  140. if( obj ){
  141. if( _isArray(obj) ){
  142. for( var i = 0, n = obj.length; i < n; i++ ){
  143. if( i in obj ){
  144. fn.call(ctx, obj[i], i, obj);
  145. }
  146. }
  147. }
  148. else {
  149. for( var key in obj ){
  150. if( obj.hasOwnProperty(key) ){
  151. fn.call(ctx, obj[key], key, obj);
  152. }
  153. }
  154. }
  155. }
  156. },
  157. /**
  158. * Merge the contents of two or more objects together into the first object
  159. */
  160. _extend = function (dst){
  161. var args = arguments, i = 1, _ext = function (val, key){ dst[key] = val; };
  162. for( ; i < args.length; i++ ){
  163. _each(args[i], _ext);
  164. }
  165. return dst;
  166. },
  167. /**
  168. * Add event listener
  169. */
  170. _on = function (el, type, fn){
  171. if( el ){
  172. var uid = api.uid(el);
  173. if( !_elEvents[uid] ){
  174. _elEvents[uid] = {};
  175. }
  176. var isFileReader = (FileReader && el) && (el instanceof FileReader);
  177. _each(type.split(/\s+/), function (type){
  178. if( jQuery && !isFileReader){
  179. jQuery.event.add(el, type, fn);
  180. } else {
  181. if( !_elEvents[uid][type] ){
  182. _elEvents[uid][type] = [];
  183. }
  184. _elEvents[uid][type].push(fn);
  185. if( el.addEventListener ){ el.addEventListener(type, fn, false); }
  186. else if( el.attachEvent ){ el.attachEvent('on'+type, fn); }
  187. else { el['on'+type] = fn; }
  188. }
  189. });
  190. }
  191. },
  192. /**
  193. * Remove event listener
  194. */
  195. _off = function (el, type, fn){
  196. if( el ){
  197. var uid = api.uid(el), events = _elEvents[uid] || {};
  198. var isFileReader = (FileReader && el) && (el instanceof FileReader);
  199. _each(type.split(/\s+/), function (type){
  200. if( jQuery && !isFileReader){
  201. jQuery.event.remove(el, type, fn);
  202. }
  203. else {
  204. var fns = events[type] || [], i = fns.length;
  205. while( i-- ){
  206. if( fns[i] === fn ){
  207. fns.splice(i, 1);
  208. break;
  209. }
  210. }
  211. if( el.addEventListener ){ el.removeEventListener(type, fn, false); }
  212. else if( el.detachEvent ){ el.detachEvent('on'+type, fn); }
  213. else { el['on'+type] = null; }
  214. }
  215. });
  216. }
  217. },
  218. _one = function(el, type, fn){
  219. _on(el, type, function _(evt){
  220. _off(el, type, _);
  221. fn(evt);
  222. });
  223. },
  224. _fixEvent = function (evt){
  225. if( !evt.target ){ evt.target = window.event && window.event.srcElement || document; }
  226. if( evt.target.nodeType === 3 ){ evt.target = evt.target.parentNode; }
  227. return evt;
  228. },
  229. _supportInputAttr = function (attr){
  230. var input = document.createElement('input');
  231. input.setAttribute('type', "file");
  232. return attr in input;
  233. },
  234. /**
  235. * FileAPI (core object)
  236. */
  237. api = {
  238. version: '2.0.7',
  239. cors: false,
  240. html5: true,
  241. media: false,
  242. formData: true,
  243. multiPassResize: true,
  244. debug: false,
  245. pingUrl: false,
  246. multiFlash: false,
  247. flashAbortTimeout: 0,
  248. withCredentials: true,
  249. staticPath: './dist/',
  250. flashUrl: 0, // @default: './FileAPI.flash.swf'
  251. flashImageUrl: 0, // @default: './FileAPI.flash.image.swf'
  252. postNameConcat: function (name, idx){
  253. return name + (idx != null ? '['+ idx +']' : '');
  254. },
  255. ext2mime: {
  256. jpg: 'image/jpeg'
  257. , tif: 'image/tiff'
  258. , txt: 'text/plain'
  259. },
  260. // Fallback for flash
  261. accept: {
  262. 'image/*': 'art bm bmp dwg dxf cbr cbz fif fpx gif ico iefs jfif jpe jpeg jpg jps jut mcf nap nif pbm pcx pgm pict pm png pnm qif qtif ras rast rf rp svf tga tif tiff xbm xbm xpm xwd'
  263. , 'audio/*': 'm4a flac aac rm mpa wav wma ogg mp3 mp2 m3u mod amf dmf dsm far gdm imf it m15 med okt s3m stm sfx ult uni xm sid ac3 dts cue aif aiff wpl ape mac mpc mpp shn wv nsf spc gym adplug adx dsp adp ymf ast afc hps xs'
  264. , 'video/*': 'm4v 3gp nsv ts ty strm rm rmvb m3u ifo mov qt divx xvid bivx vob nrg img iso pva wmv asf asx ogm m2v avi bin dat dvr-ms mpg mpeg mp4 mkv avc vp3 svq3 nuv viv dv fli flv wpl'
  265. },
  266. uploadRetry : 0,
  267. networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down
  268. chunkSize : 0,
  269. chunkUploadRetry : 0,
  270. chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down
  271. KB: _SIZE_CONST(1),
  272. MB: _SIZE_CONST(2),
  273. GB: _SIZE_CONST(3),
  274. TB: _SIZE_CONST(4),
  275. EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=',
  276. expando: 'fileapi' + (new Date).getTime(),
  277. uid: function (obj){
  278. return obj
  279. ? (obj[api.expando] = obj[api.expando] || api.uid())
  280. : (++gid, api.expando + gid)
  281. ;
  282. },
  283. log: function (){
  284. // ngf fix for IE8 #1071
  285. if( api.debug && api._supportConsoleLog ){
  286. if( api._supportConsoleLogApply ){
  287. console.log.apply(console, arguments);
  288. }
  289. else {
  290. console.log([].join.call(arguments, ' '));
  291. }
  292. }
  293. },
  294. /**
  295. * Create new image
  296. *
  297. * @param {String} [src]
  298. * @param {Function} [fn] 1. error -- boolean, 2. img -- Image element
  299. * @returns {HTMLElement}
  300. */
  301. newImage: function (src, fn){
  302. var img = document.createElement('img');
  303. if( fn ){
  304. api.event.one(img, 'error load', function (evt){
  305. fn(evt.type == 'error', img);
  306. img = null;
  307. });
  308. }
  309. img.src = src;
  310. return img;
  311. },
  312. /**
  313. * Get XHR
  314. * @returns {XMLHttpRequest}
  315. */
  316. getXHR: function (){
  317. var xhr;
  318. if( XMLHttpRequest ){
  319. xhr = new XMLHttpRequest;
  320. }
  321. else if( window.ActiveXObject ){
  322. try {
  323. xhr = new ActiveXObject('MSXML2.XMLHttp.3.0');
  324. } catch (e) {
  325. xhr = new ActiveXObject('Microsoft.XMLHTTP');
  326. }
  327. }
  328. return xhr;
  329. },
  330. isArray: _isArray,
  331. support: {
  332. dnd: cors && ('ondrop' in document.createElement('div')),
  333. cors: cors,
  334. html5: html5,
  335. chunked: chunked,
  336. dataURI: true,
  337. accept: _supportInputAttr('accept'),
  338. multiple: _supportInputAttr('multiple')
  339. },
  340. event: {
  341. on: _on
  342. , off: _off
  343. , one: _one
  344. , fix: _fixEvent
  345. },
  346. throttle: function(fn, delay) {
  347. var id, args;
  348. return function _throttle(){
  349. args = arguments;
  350. if( !id ){
  351. fn.apply(window, args);
  352. id = setTimeout(function (){
  353. id = 0;
  354. fn.apply(window, args);
  355. }, delay);
  356. }
  357. };
  358. },
  359. F: function (){},
  360. parseJSON: function (str){
  361. var json;
  362. if( window.JSON && JSON.parse ){
  363. json = JSON.parse(str);
  364. }
  365. else {
  366. json = (new Function('return ('+str.replace(/([\r\n])/g, '\\$1')+');'))();
  367. }
  368. return json;
  369. },
  370. trim: function (str){
  371. str = String(str);
  372. return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  373. },
  374. /**
  375. * Simple Defer
  376. * @return {Object}
  377. */
  378. defer: function (){
  379. var
  380. list = []
  381. , result
  382. , error
  383. , defer = {
  384. resolve: function (err, res){
  385. defer.resolve = noop;
  386. error = err || false;
  387. result = res;
  388. while( res = list.shift() ){
  389. res(error, result);
  390. }
  391. },
  392. then: function (fn){
  393. if( error !== undef ){
  394. fn(error, result);
  395. } else {
  396. list.push(fn);
  397. }
  398. }
  399. };
  400. return defer;
  401. },
  402. queue: function (fn){
  403. var
  404. _idx = 0
  405. , _length = 0
  406. , _fail = false
  407. , _end = false
  408. , queue = {
  409. inc: function (){
  410. _length++;
  411. },
  412. next: function (){
  413. _idx++;
  414. setTimeout(queue.check, 0);
  415. },
  416. check: function (){
  417. (_idx >= _length) && !_fail && queue.end();
  418. },
  419. isFail: function (){
  420. return _fail;
  421. },
  422. fail: function (){
  423. !_fail && fn(_fail = true);
  424. },
  425. end: function (){
  426. if( !_end ){
  427. _end = true;
  428. fn();
  429. }
  430. }
  431. }
  432. ;
  433. return queue;
  434. },
  435. /**
  436. * For each object
  437. *
  438. * @param {Object|Array} obj
  439. * @param {Function} fn
  440. * @param {*} [ctx]
  441. */
  442. each: _each,
  443. /**
  444. * Async for
  445. * @param {Array} array
  446. * @param {Function} callback
  447. */
  448. afor: function (array, callback){
  449. var i = 0, n = array.length;
  450. if( _isArray(array) && n-- ){
  451. (function _next(){
  452. callback(n != i && _next, array[i], i++);
  453. })();
  454. }
  455. else {
  456. callback(false);
  457. }
  458. },
  459. /**
  460. * Merge the contents of two or more objects together into the first object
  461. *
  462. * @param {Object} dst
  463. * @return {Object}
  464. */
  465. extend: _extend,
  466. /**
  467. * Is file?
  468. * @param {File} file
  469. * @return {Boolean}
  470. */
  471. isFile: function (file){
  472. return _toString.call(file) === '[object File]';
  473. },
  474. /**
  475. * Is blob?
  476. * @param {Blob} blob
  477. * @returns {Boolean}
  478. */
  479. isBlob: function (blob) {
  480. return this.isFile(blob) || (_toString.call(blob) === '[object Blob]');
  481. },
  482. /**
  483. * Is canvas element
  484. *
  485. * @param {HTMLElement} el
  486. * @return {Boolean}
  487. */
  488. isCanvas: function (el){
  489. return el && _rcanvas.test(el.nodeName);
  490. },
  491. getFilesFilter: function (filter){
  492. filter = typeof filter == 'string' ? filter : (filter.getAttribute && filter.getAttribute('accept') || '');
  493. return filter ? new RegExp('('+ filter.replace(/\./g, '\\.').replace(/,/g, '|') +')$', 'i') : /./;
  494. },
  495. /**
  496. * Read as DataURL
  497. *
  498. * @param {File|Element} file
  499. * @param {Function} fn
  500. */
  501. readAsDataURL: function (file, fn){
  502. if( api.isCanvas(file) ){
  503. _emit(file, fn, 'load', api.toDataURL(file));
  504. }
  505. else {
  506. _readAs(file, fn, 'DataURL');
  507. }
  508. },
  509. /**
  510. * Read as Binary string
  511. *
  512. * @param {File} file
  513. * @param {Function} fn
  514. */
  515. readAsBinaryString: function (file, fn){
  516. if( _hasSupportReadAs('BinaryString') ){
  517. _readAs(file, fn, 'BinaryString');
  518. } else {
  519. // Hello IE10!
  520. _readAs(file, function (evt){
  521. if( evt.type == 'load' ){
  522. try {
  523. // dataURL -> binaryString
  524. evt.result = api.toBinaryString(evt.result);
  525. } catch (e){
  526. evt.type = 'error';
  527. evt.message = e.toString();
  528. }
  529. }
  530. fn(evt);
  531. }, 'DataURL');
  532. }
  533. },
  534. /**
  535. * Read as ArrayBuffer
  536. *
  537. * @param {File} file
  538. * @param {Function} fn
  539. */
  540. readAsArrayBuffer: function(file, fn){
  541. _readAs(file, fn, 'ArrayBuffer');
  542. },
  543. /**
  544. * Read as text
  545. *
  546. * @param {File} file
  547. * @param {String} encoding
  548. * @param {Function} [fn]
  549. */
  550. readAsText: function(file, encoding, fn){
  551. if( !fn ){
  552. fn = encoding;
  553. encoding = 'utf-8';
  554. }
  555. _readAs(file, fn, 'Text', encoding);
  556. },
  557. /**
  558. * Convert image or canvas to DataURL
  559. *
  560. * @param {Element} el Image or Canvas element
  561. * @param {String} [type] mime-type
  562. * @return {String}
  563. */
  564. toDataURL: function (el, type){
  565. if( typeof el == 'string' ){
  566. return el;
  567. }
  568. else if( el.toDataURL ){
  569. return el.toDataURL(type || 'image/png');
  570. }
  571. },
  572. /**
  573. * Canvert string, image or canvas to binary string
  574. *
  575. * @param {String|Element} val
  576. * @return {String}
  577. */
  578. toBinaryString: function (val){
  579. return window.atob(api.toDataURL(val).replace(_rdata, ''));
  580. },
  581. /**
  582. * Read file or DataURL as ImageElement
  583. *
  584. * @param {File|String} file
  585. * @param {Function} fn
  586. * @param {Boolean} [progress]
  587. */
  588. readAsImage: function (file, fn, progress){
  589. if( api.isFile(file) ){
  590. if( apiURL ){
  591. /** @namespace apiURL.createObjectURL */
  592. var data = apiURL.createObjectURL(file);
  593. if( data === undef ){
  594. _emit(file, fn, 'error');
  595. }
  596. else {
  597. api.readAsImage(data, fn, progress);
  598. }
  599. }
  600. else {
  601. api.readAsDataURL(file, function (evt){
  602. if( evt.type == 'load' ){
  603. api.readAsImage(evt.result, fn, progress);
  604. }
  605. else if( progress || evt.type == 'error' ){
  606. _emit(file, fn, evt, null, { loaded: evt.loaded, total: evt.total });
  607. }
  608. });
  609. }
  610. }
  611. else if( api.isCanvas(file) ){
  612. _emit(file, fn, 'load', file);
  613. }
  614. else if( _rimg.test(file.nodeName) ){
  615. if( file.complete ){
  616. _emit(file, fn, 'load', file);
  617. }
  618. else {
  619. var events = 'error abort load';
  620. _one(file, events, function _fn(evt){
  621. if( evt.type == 'load' && apiURL ){
  622. /** @namespace apiURL.revokeObjectURL */
  623. apiURL.revokeObjectURL(file.src);
  624. }
  625. _off(file, events, _fn);
  626. _emit(file, fn, evt, file);
  627. });
  628. }
  629. }
  630. else if( file.iframe ){
  631. _emit(file, fn, { type: 'error' });
  632. }
  633. else {
  634. // Created image
  635. var img = api.newImage(file.dataURL || file);
  636. api.readAsImage(img, fn, progress);
  637. }
  638. },
  639. /**
  640. * Make file by name
  641. *
  642. * @param {String} name
  643. * @return {Array}
  644. */
  645. checkFileObj: function (name){
  646. var file = {}, accept = api.accept;
  647. if( typeof name == 'object' ){
  648. file = name;
  649. }
  650. else {
  651. file.name = (name + '').split(/\\|\//g).pop();
  652. }
  653. if( file.type == null ){
  654. file.type = file.name.split('.').pop();
  655. }
  656. _each(accept, function (ext, type){
  657. ext = new RegExp(ext.replace(/\s/g, '|'), 'i');
  658. if( ext.test(file.type) || api.ext2mime[file.type] ){
  659. file.type = api.ext2mime[file.type] || (type.split('/')[0] +'/'+ file.type);
  660. }
  661. });
  662. return file;
  663. },
  664. /**
  665. * Get drop files
  666. *
  667. * @param {Event} evt
  668. * @param {Function} callback
  669. */
  670. getDropFiles: function (evt, callback){
  671. var
  672. files = []
  673. , dataTransfer = _getDataTransfer(evt)
  674. , entrySupport = _isArray(dataTransfer.items) && dataTransfer.items[0] && _getAsEntry(dataTransfer.items[0])
  675. , queue = api.queue(function (){ callback(files); })
  676. ;
  677. _each((entrySupport ? dataTransfer.items : dataTransfer.files) || [], function (item){
  678. queue.inc();
  679. try {
  680. if( entrySupport ){
  681. _readEntryAsFiles(item, function (err, entryFiles){
  682. if( err ){
  683. api.log('[err] getDropFiles:', err);
  684. } else {
  685. files.push.apply(files, entryFiles);
  686. }
  687. queue.next();
  688. });
  689. }
  690. else {
  691. _isRegularFile(item, function (yes){
  692. yes && files.push(item);
  693. queue.next();
  694. });
  695. }
  696. }
  697. catch( err ){
  698. queue.next();
  699. api.log('[err] getDropFiles: ', err);
  700. }
  701. });
  702. queue.check();
  703. },
  704. /**
  705. * Get file list
  706. *
  707. * @param {HTMLInputElement|Event} input
  708. * @param {String|Function} [filter]
  709. * @param {Function} [callback]
  710. * @return {Array|Null}
  711. */
  712. getFiles: function (input, filter, callback){
  713. var files = [];
  714. if( callback ){
  715. api.filterFiles(api.getFiles(input), filter, callback);
  716. return null;
  717. }
  718. if( input.jquery ){
  719. // jQuery object
  720. input.each(function (){
  721. files = files.concat(api.getFiles(this));
  722. });
  723. input = files;
  724. files = [];
  725. }
  726. if( typeof filter == 'string' ){
  727. filter = api.getFilesFilter(filter);
  728. }
  729. if( input.originalEvent ){
  730. // jQuery event
  731. input = _fixEvent(input.originalEvent);
  732. }
  733. else if( input.srcElement ){
  734. // IE Event
  735. input = _fixEvent(input);
  736. }
  737. if( input.dataTransfer ){
  738. // Drag'n'Drop
  739. input = input.dataTransfer;
  740. }
  741. else if( input.target ){
  742. // Event
  743. input = input.target;
  744. }
  745. if( input.files ){
  746. // Input[type="file"]
  747. files = input.files;
  748. if( !html5 ){
  749. // Partial support for file api
  750. files[0].blob = input;
  751. files[0].iframe = true;
  752. }
  753. }
  754. else if( !html5 && isInputFile(input) ){
  755. if( api.trim(input.value) ){
  756. files = [api.checkFileObj(input.value)];
  757. files[0].blob = input;
  758. files[0].iframe = true;
  759. }
  760. }
  761. else if( _isArray(input) ){
  762. files = input;
  763. }
  764. return api.filter(files, function (file){ return !filter || filter.test(file.name); });
  765. },
  766. /**
  767. * Get total file size
  768. * @param {Array} files
  769. * @return {Number}
  770. */
  771. getTotalSize: function (files){
  772. var size = 0, i = files && files.length;
  773. while( i-- ){
  774. size += files[i].size;
  775. }
  776. return size;
  777. },
  778. /**
  779. * Get image information
  780. *
  781. * @param {File} file
  782. * @param {Function} fn
  783. */
  784. getInfo: function (file, fn){
  785. var info = {}, readers = _infoReader.concat();
  786. if( api.isFile(file) ){
  787. (function _next(){
  788. var reader = readers.shift();
  789. if( reader ){
  790. if( reader.test(file.type) ){
  791. reader(file, function (err, res){
  792. if( err ){
  793. fn(err);
  794. }
  795. else {
  796. _extend(info, res);
  797. _next();
  798. }
  799. });
  800. }
  801. else {
  802. _next();
  803. }
  804. }
  805. else {
  806. fn(false, info);
  807. }
  808. })();
  809. }
  810. else {
  811. fn('not_support_info', info);
  812. }
  813. },
  814. /**
  815. * Add information reader
  816. *
  817. * @param {RegExp} mime
  818. * @param {Function} fn
  819. */
  820. addInfoReader: function (mime, fn){
  821. fn.test = function (type){ return mime.test(type); };
  822. _infoReader.push(fn);
  823. },
  824. /**
  825. * Filter of array
  826. *
  827. * @param {Array} input
  828. * @param {Function} fn
  829. * @return {Array}
  830. */
  831. filter: function (input, fn){
  832. var result = [], i = 0, n = input.length, val;
  833. for( ; i < n; i++ ){
  834. if( i in input ){
  835. val = input[i];
  836. if( fn.call(val, val, i, input) ){
  837. result.push(val);
  838. }
  839. }
  840. }
  841. return result;
  842. },
  843. /**
  844. * Filter files
  845. *
  846. * @param {Array} files
  847. * @param {Function} eachFn
  848. * @param {Function} resultFn
  849. */
  850. filterFiles: function (files, eachFn, resultFn){
  851. if( files.length ){
  852. // HTML5 or Flash
  853. var queue = files.concat(), file, result = [], deleted = [];
  854. (function _next(){
  855. if( queue.length ){
  856. file = queue.shift();
  857. api.getInfo(file, function (err, info){
  858. (eachFn(file, err ? false : info) ? result : deleted).push(file);
  859. _next();
  860. });
  861. }
  862. else {
  863. resultFn(result, deleted);
  864. }
  865. })();
  866. }
  867. else {
  868. resultFn([], files);
  869. }
  870. },
  871. upload: function (options){
  872. options = _extend({
  873. jsonp: 'callback'
  874. , prepare: api.F
  875. , beforeupload: api.F
  876. , upload: api.F
  877. , fileupload: api.F
  878. , fileprogress: api.F
  879. , filecomplete: api.F
  880. , progress: api.F
  881. , complete: api.F
  882. , pause: api.F
  883. , imageOriginal: true
  884. , chunkSize: api.chunkSize
  885. , chunkUploadRetry: api.chunkUploadRetry
  886. , uploadRetry: api.uploadRetry
  887. }, options);
  888. if( options.imageAutoOrientation && !options.imageTransform ){
  889. options.imageTransform = { rotate: 'auto' };
  890. }
  891. var
  892. proxyXHR = new api.XHR(options)
  893. , dataArray = this._getFilesDataArray(options.files)
  894. , _this = this
  895. , _total = 0
  896. , _loaded = 0
  897. , _nextFile
  898. , _complete = false
  899. ;
  900. // calc total size
  901. _each(dataArray, function (data){
  902. _total += data.size;
  903. });
  904. // Array of files
  905. proxyXHR.files = [];
  906. _each(dataArray, function (data){
  907. proxyXHR.files.push(data.file);
  908. });
  909. // Set upload status props
  910. proxyXHR.total = _total;
  911. proxyXHR.loaded = 0;
  912. proxyXHR.filesLeft = dataArray.length;
  913. // emit "beforeupload" event
  914. options.beforeupload(proxyXHR, options);
  915. // Upload by file
  916. _nextFile = function (){
  917. var
  918. data = dataArray.shift()
  919. , _file = data && data.file
  920. , _fileLoaded = false
  921. , _fileOptions = _simpleClone(options)
  922. ;
  923. proxyXHR.filesLeft = dataArray.length;
  924. if( _file && _file.name === api.expando ){
  925. _file = null;
  926. api.log('[warn] FileAPI.upload() — called without files');
  927. }
  928. if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){
  929. // Mark active job
  930. _complete = false;
  931. // Set current upload file
  932. proxyXHR.currentFile = _file;
  933. // Prepare file options
  934. if (_file && options.prepare(_file, _fileOptions) === false) {
  935. _nextFile.call(_this);
  936. return;
  937. }
  938. _fileOptions.file = _file;
  939. _this._getFormData(_fileOptions, data, function (form){
  940. if( !_loaded ){
  941. // emit "upload" event
  942. options.upload(proxyXHR, options);
  943. }
  944. var xhr = new api.XHR(_extend({}, _fileOptions, {
  945. upload: _file ? function (){
  946. // emit "fileupload" event
  947. options.fileupload(_file, xhr, _fileOptions);
  948. } : noop,
  949. progress: _file ? function (evt){
  950. if( !_fileLoaded ){
  951. // For ignore the double calls.
  952. _fileLoaded = (evt.loaded === evt.total);
  953. // emit "fileprogress" event
  954. options.fileprogress({
  955. type: 'progress'
  956. , total: data.total = evt.total
  957. , loaded: data.loaded = evt.loaded
  958. }, _file, xhr, _fileOptions);
  959. // emit "progress" event
  960. options.progress({
  961. type: 'progress'
  962. , total: _total
  963. , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0
  964. }, _file, xhr, _fileOptions);
  965. }
  966. } : noop,
  967. complete: function (err){
  968. _each(_xhrPropsExport, function (name){
  969. proxyXHR[name] = xhr[name];
  970. });
  971. if( _file ){
  972. data.total = (data.total || data.size);
  973. data.loaded = data.total;
  974. if( !err ) {
  975. // emulate 100% "progress"
  976. this.progress(data);
  977. // fixed throttle event
  978. _fileLoaded = true;
  979. // bytes loaded
  980. _loaded += data.size; // data.size != data.total, it's desirable fix this
  981. proxyXHR.loaded = _loaded;
  982. }
  983. // emit "filecomplete" event
  984. options.filecomplete(err, xhr, _file, _fileOptions);
  985. }
  986. // upload next file
  987. setTimeout(function () {_nextFile.call(_this);}, 0);
  988. }
  989. })); // xhr
  990. // ...
  991. proxyXHR.abort = function (current){
  992. if (!current) { dataArray.length = 0; }
  993. this.current = current;
  994. xhr.abort();
  995. };
  996. // Start upload
  997. xhr.send(form);
  998. });
  999. }
  1000. else {
  1001. var successful = proxyXHR.status == 200 || proxyXHR.status == 201 || proxyXHR.status == 204;
  1002. options.complete(successful ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
  1003. // Mark done state
  1004. _complete = true;
  1005. }
  1006. };
  1007. // Next tick
  1008. setTimeout(_nextFile, 0);
  1009. // Append more files to the existing request
  1010. // first - add them to the queue head/tail
  1011. proxyXHR.append = function (files, first) {
  1012. files = api._getFilesDataArray([].concat(files));
  1013. _each(files, function (data) {
  1014. _total += data.size;
  1015. proxyXHR.files.push(data.file);
  1016. if (first) {
  1017. dataArray.unshift(data);
  1018. } else {
  1019. dataArray.push(data);
  1020. }
  1021. });
  1022. proxyXHR.statusText = "";
  1023. if( _complete ){
  1024. _nextFile.call(_this);
  1025. }
  1026. };
  1027. // Removes file from queue by file reference and returns it
  1028. proxyXHR.remove = function (file) {
  1029. var i = dataArray.length, _file;
  1030. while( i-- ){
  1031. if( dataArray[i].file == file ){
  1032. _file = dataArray.splice(i, 1);
  1033. _total -= _file.size;
  1034. }
  1035. }
  1036. return _file;
  1037. };
  1038. return proxyXHR;
  1039. },
  1040. _getFilesDataArray: function (data){
  1041. var files = [], oFiles = {};
  1042. if( isInputFile(data) ){
  1043. var tmp = api.getFiles(data);
  1044. oFiles[data.name || 'file'] = data.getAttribute('multiple') !== null ? tmp : tmp[0];
  1045. }
  1046. else if( _isArray(data) && isInputFile(data[0]) ){
  1047. _each(data, function (input){
  1048. oFiles[input.name || 'file'] = api.getFiles(input);
  1049. });
  1050. }
  1051. else {
  1052. oFiles = data;
  1053. }
  1054. _each(oFiles, function add(file, name){
  1055. if( _isArray(file) ){
  1056. _each(file, function (file){
  1057. add(file, name);
  1058. });
  1059. }
  1060. else if( file && (file.name || file.image) ){
  1061. files.push({
  1062. name: name
  1063. , file: file
  1064. , size: file.size
  1065. , total: file.size
  1066. , loaded: 0
  1067. });
  1068. }
  1069. });
  1070. if( !files.length ){
  1071. // Create fake `file` object
  1072. files.push({ file: { name: api.expando } });
  1073. }
  1074. return files;
  1075. },
  1076. _getFormData: function (options, data, fn){
  1077. var
  1078. file = data.file
  1079. , name = data.name
  1080. , filename = file.name
  1081. , filetype = file.type
  1082. , trans = api.support.transform && options.imageTransform
  1083. , Form = new api.Form
  1084. , queue = api.queue(function (){ fn(Form); })
  1085. , isOrignTrans = trans && _isOriginTransform(trans)
  1086. , postNameConcat = api.postNameConcat
  1087. ;
  1088. // Append data
  1089. _each(options.data, function add(val, name){
  1090. if( typeof val == 'object' ){
  1091. _each(val, function (v, i){
  1092. add(v, postNameConcat(name, i));
  1093. });
  1094. }
  1095. else {
  1096. Form.append(name, val);
  1097. }
  1098. });
  1099. (function _addFile(file/**Object*/){
  1100. if( file.image ){ // This is a FileAPI.Image
  1101. queue.inc();
  1102. file.toData(function (err, image){
  1103. // @todo: error
  1104. filename = filename || (new Date).getTime()+'.png';
  1105. _addFile(image);
  1106. queue.next();
  1107. });
  1108. }
  1109. else if( api.Image && trans && (/^image/.test(file.type) || _rimgcanvas.test(file.nodeName)) ){
  1110. queue.inc();
  1111. if( isOrignTrans ){
  1112. // Convert to array for transform function
  1113. trans = [trans];
  1114. }
  1115. api.Image.transform(file, trans, options.imageAutoOrientation, function (err, images){
  1116. if( isOrignTrans && !err ){
  1117. if( !dataURLtoBlob && !api.flashEngine ){
  1118. // Canvas.toBlob or Flash not supported, use multipart
  1119. Form.multipart = true;
  1120. }
  1121. Form.append(name, images[0], filename, trans[0].type || filetype);
  1122. }
  1123. else {
  1124. var addOrigin = 0;
  1125. if( !err ){
  1126. _each(images, function (image, idx){
  1127. if( !dataURLtoBlob && !api.flashEngine ){
  1128. Form.multipart = true;
  1129. }
  1130. if( !trans[idx].postName ){
  1131. addOrigin = 1;
  1132. }
  1133. Form.append(trans[idx].postName || postNameConcat(name, idx), image, filename, trans[idx].type || filetype);
  1134. });
  1135. }
  1136. if( err || options.imageOriginal ){
  1137. Form.append(postNameConcat(name, (addOrigin ? 'original' : null)), file, filename, filetype);
  1138. }
  1139. }
  1140. queue.next();
  1141. });
  1142. }
  1143. else if( filename !== api.expando ){
  1144. Form.append(name, file, filename);
  1145. }
  1146. })(file);
  1147. queue.check();
  1148. },
  1149. reset: function (inp, notRemove){
  1150. var parent, clone;
  1151. if( jQuery ){
  1152. clone = jQuery(inp).clone(true).insertBefore(inp).val('')[0];
  1153. if( !notRemove ){
  1154. jQuery(inp).remove();
  1155. }
  1156. } else {
  1157. parent = inp.parentNode;
  1158. clone = parent.insertBefore(inp.cloneNode(true), inp);
  1159. clone.value = '';
  1160. if( !notRemove ){
  1161. parent.removeChild(inp);
  1162. }
  1163. _each(_elEvents[api.uid(inp)], function (fns, type){
  1164. _each(fns, function (fn){
  1165. _off(inp, type, fn);
  1166. _on(clone, type, fn);
  1167. });
  1168. });
  1169. }
  1170. return clone;
  1171. },
  1172. /**
  1173. * Load remote file
  1174. *
  1175. * @param {String} url
  1176. * @param {Function} fn
  1177. * @return {XMLHttpRequest}
  1178. */
  1179. load: function (url, fn){
  1180. var xhr = api.getXHR();
  1181. if( xhr ){
  1182. xhr.open('GET', url, true);
  1183. if( xhr.overrideMimeType ){
  1184. xhr.overrideMimeType('text/plain; charset=x-user-defined');
  1185. }
  1186. _on(xhr, 'progress', function (/**Event*/evt){
  1187. /** @namespace evt.lengthComputable */
  1188. if( evt.lengthComputable ){
  1189. fn({ type: evt.type, loaded: evt.loaded, total: evt.total }, xhr);
  1190. }
  1191. });
  1192. xhr.onreadystatechange = function(){
  1193. if( xhr.readyState == 4 ){
  1194. xhr.onreadystatechange = null;
  1195. if( xhr.status == 200 ){
  1196. url = url.split('/');
  1197. /** @namespace xhr.responseBody */
  1198. var file = {
  1199. name: url[url.length-1]
  1200. , size: xhr.getResponseHeader('Content-Length')
  1201. , type: xhr.getResponseHeader('Content-Type')
  1202. };
  1203. file.dataURL = 'data:'+file.type+';base64,' + api.encode64(xhr.responseBody || xhr.responseText);
  1204. fn({ type: 'load', result: file }, xhr);
  1205. }
  1206. else {
  1207. fn({ type: 'error' }, xhr);
  1208. }
  1209. }
  1210. };
  1211. xhr.send(null);
  1212. } else {
  1213. fn({ type: 'error' });
  1214. }
  1215. return xhr;
  1216. },
  1217. encode64: function (str){
  1218. var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', outStr = '', i = 0;
  1219. if( typeof str !== 'string' ){
  1220. str = String(str);
  1221. }
  1222. while( i < str.length ){
  1223. //all three "& 0xff" added below are there to fix a known bug
  1224. //with bytes returned by xhr.responseText
  1225. var
  1226. byte1 = str.charCodeAt(i++) & 0xff
  1227. , byte2 = str.charCodeAt(i++) & 0xff
  1228. , byte3 = str.charCodeAt(i++) & 0xff
  1229. , enc1 = byte1 >> 2
  1230. , enc2 = ((byte1 & 3) << 4) | (byte2 >> 4)
  1231. , enc3, enc4
  1232. ;
  1233. if( isNaN(byte2) ){
  1234. enc3 = enc4 = 64;
  1235. } else {
  1236. enc3 = ((byte2 & 15) << 2) | (byte3 >> 6);
  1237. enc4 = isNaN(byte3) ? 64 : byte3 & 63;
  1238. }
  1239. outStr += b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4);
  1240. }
  1241. return outStr;
  1242. }
  1243. } // api
  1244. ;
  1245. function _emit(target, fn, name, res, ext){
  1246. var evt = {
  1247. type: name.type || name
  1248. , target: target
  1249. , result: res
  1250. };
  1251. _extend(evt, ext);
  1252. fn(evt);
  1253. }
  1254. function _hasSupportReadAs(as){
  1255. return FileReader && !!FileReader.prototype['readAs'+as];
  1256. }
  1257. function _readAs(file, fn, as, encoding){
  1258. if( api.isBlob(file) && _hasSupportReadAs(as) ){
  1259. var Reader = new FileReader;
  1260. // Add event listener
  1261. _on(Reader, _readerEvents, function _fn(evt){
  1262. var type = evt.type;
  1263. if( type == 'progress' ){
  1264. _emit(file, fn, evt, evt.target.result, { loaded: evt.loaded, total: evt.total });
  1265. }
  1266. else if( type == 'loadend' ){
  1267. _off(Reader, _readerEvents, _fn);
  1268. Reader = null;
  1269. }
  1270. else {
  1271. _emit(file, fn, evt, evt.target.result);
  1272. }
  1273. });
  1274. try {
  1275. // ReadAs ...
  1276. if( encoding ){
  1277. Reader['readAs'+as](file, encoding);
  1278. }
  1279. else {
  1280. Reader['readAs'+as](file);
  1281. }
  1282. }
  1283. catch (err){
  1284. _emit(file, fn, 'error', undef, { error: err.toString() });
  1285. }
  1286. }
  1287. else {
  1288. _emit(file, fn, 'error', undef, { error: 'FileReader_not_support_'+as });
  1289. }
  1290. }
  1291. function _isRegularFile(file, callback){
  1292. // http://stackoverflow.com/questions/8856628/detecting-folders-directories-in-javascript-filelist-objects
  1293. if( !file.type && (file.size % 4096) === 0 && (file.size <= 102400) ){
  1294. if( FileReader ){
  1295. try {
  1296. var Reader = new FileReader();
  1297. _one(Reader, _readerEvents, function (evt){
  1298. var isFile = evt.type != 'error';
  1299. callback(isFile);
  1300. if( isFile ){
  1301. Reader.abort();
  1302. }
  1303. });
  1304. Reader.readAsDataURL(file);
  1305. } catch( err ){
  1306. callback(false);
  1307. }
  1308. }
  1309. else {
  1310. callback(null);
  1311. }
  1312. }
  1313. else {
  1314. callback(true);
  1315. }
  1316. }
  1317. function _getAsEntry(item){
  1318. var entry;
  1319. if( item.getAsEntry ){ entry = item.getAsEntry(); }
  1320. else if( item.webkitGetAsEntry ){ entry = item.webkitGetAsEntry(); }
  1321. return entry;
  1322. }
  1323. function _readEntryAsFiles(entry, callback){
  1324. if( !entry ){
  1325. // error
  1326. callback('invalid entry');
  1327. }
  1328. else if( entry.isFile ){
  1329. // Read as file
  1330. entry.file(function(file){
  1331. // success
  1332. file.fullPath = entry.fullPath;
  1333. callback(false, [file]);
  1334. }, function (err){
  1335. // error
  1336. callback('FileError.code: '+err.code);
  1337. });
  1338. }
  1339. else if( entry.isDirectory ){
  1340. var reader = entry.createReader(), result = [];
  1341. reader.readEntries(function(entries){
  1342. // success
  1343. api.afor(entries, function (next, entry){
  1344. _readEntryAsFiles(entry, function (err, files){
  1345. if( err ){
  1346. api.log(err);
  1347. }
  1348. else {
  1349. result = result.concat(files);
  1350. }
  1351. if( next ){
  1352. next();
  1353. }
  1354. else {
  1355. callback(false, result);
  1356. }
  1357. });
  1358. });
  1359. }, function (err){
  1360. // error
  1361. callback('directory_reader: ' + err);
  1362. });
  1363. }
  1364. else {
  1365. _readEntryAsFiles(_getAsEntry(entry), callback);
  1366. }
  1367. }
  1368. function _simpleClone(obj){
  1369. var copy = {};
  1370. _each(obj, function (val, key){
  1371. if( val && (typeof val === 'object') && (val.nodeType === void 0) ){
  1372. val = _extend({}, val);
  1373. }
  1374. copy[key] = val;
  1375. });
  1376. return copy;
  1377. }
  1378. function isInputFile(el){
  1379. return _rinput.test(el && el.tagName);
  1380. }
  1381. function _getDataTransfer(evt){
  1382. return (evt.originalEvent || evt || '').dataTransfer || {};
  1383. }
  1384. function _isOriginTransform(trans){
  1385. var key;
  1386. for( key in trans ){
  1387. if( trans.hasOwnProperty(key) ){
  1388. if( !(trans[key] instanceof Object || key === 'overlay' || key === 'filter') ){
  1389. return true;
  1390. }
  1391. }
  1392. }
  1393. return false;
  1394. }
  1395. // Add default image info reader
  1396. api.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){
  1397. if( !file.__dimensions ){
  1398. var defer = file.__dimensions = api.defer();
  1399. api.readAsImage(file, function (evt){
  1400. var img = evt.target;
  1401. defer.resolve(evt.type == 'load' ? false : 'error', {
  1402. width: img.width
  1403. , height: img.height
  1404. });
  1405. img.src = api.EMPTY_PNG;
  1406. img = null;
  1407. });
  1408. }
  1409. file.__dimensions.then(callback);
  1410. });
  1411. /**
  1412. * Drag'n'Drop special event
  1413. *
  1414. * @param {HTMLElement} el
  1415. * @param {Function} onHover
  1416. * @param {Function} onDrop
  1417. */
  1418. api.event.dnd = function (el, onHover, onDrop){
  1419. var _id, _type;
  1420. if( !onDrop ){
  1421. onDrop = onHover;
  1422. onHover = api.F;
  1423. }
  1424. if( FileReader ){
  1425. // Hover
  1426. _on(el, 'dragenter dragleave dragover', onHover.ff = onHover.ff || function (evt){
  1427. var
  1428. types = _getDataTransfer(evt).types
  1429. , i = types && types.length
  1430. , debounceTrigger = false
  1431. ;
  1432. while( i-- ){
  1433. if( ~types[i].indexOf('File') ){
  1434. evt[preventDefault]();
  1435. if( _type !== evt.type ){
  1436. _type = evt.type; // Store current type of event
  1437. if( _type != 'dragleave' ){
  1438. onHover.call(evt[currentTarget], true, evt);
  1439. }
  1440. debounceTrigger = true;
  1441. }
  1442. break; // exit from "while"
  1443. }
  1444. }
  1445. if( debounceTrigger ){
  1446. clearTimeout(_id);
  1447. _id = setTimeout(function (){
  1448. onHover.call(evt[currentTarget], _type != 'dragleave', evt);
  1449. }, 50);
  1450. }
  1451. });
  1452. // Drop
  1453. _on(el, 'drop', onDrop.ff = onDrop.ff || function (evt){
  1454. evt[preventDefault]();
  1455. _type = 0;
  1456. onHover.call(evt[currentTarget], false, evt);
  1457. api.getDropFiles(evt, function (files){
  1458. onDrop.call(evt[currentTarget], files, evt);
  1459. });
  1460. });
  1461. }
  1462. else {
  1463. api.log("Drag'n'Drop -- not supported");
  1464. }
  1465. };
  1466. /**
  1467. * Remove drag'n'drop
  1468. * @param {HTMLElement} el
  1469. * @param {Function} onHover
  1470. * @param {Function} onDrop
  1471. */
  1472. api.event.dnd.off = function (el, onHover, onDrop){
  1473. _off(el, 'dragenter dragleave dragover', onHover.ff);
  1474. _off(el, 'drop', onDrop.ff);
  1475. };
  1476. // Support jQuery
  1477. if( jQuery && !jQuery.fn.dnd ){
  1478. jQuery.fn.dnd = function (onHover, onDrop){
  1479. return this.each(function (){
  1480. api.event.dnd(this, onHover, onDrop);
  1481. });
  1482. };
  1483. jQuery.fn.offdnd = function (onHover, onDrop){
  1484. return this.each(function (){
  1485. api.event.dnd.off(this, onHover, onDrop);
  1486. });
  1487. };
  1488. }
  1489. // @export
  1490. window.FileAPI = _extend(api, window.FileAPI);
  1491. // Debug info
  1492. api.log('FileAPI: ' + api.version);
  1493. api.log('protocol: ' + window.location.protocol);
  1494. api.log('doctype: [' + doctype.name + '] ' + doctype.publicId + ' ' + doctype.systemId);
  1495. // @detect 'x-ua-compatible'
  1496. _each(document.getElementsByTagName('meta'), function (meta){
  1497. if( /x-ua-compatible/i.test(meta.getAttribute('http-equiv')) ){
  1498. api.log('meta.http-equiv: ' + meta.getAttribute('content'));
  1499. }
  1500. });
  1501. // configuration
  1502. try {
  1503. api._supportConsoleLog = !!console.log;
  1504. api._supportConsoleLogApply = !!console.log.apply;
  1505. } catch (err) {}
  1506. if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; }
  1507. if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; }
  1508. if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; }
  1509. })(window, void 0);
  1510. /*global window, FileAPI, document */
  1511. (function (api, document, undef) {
  1512. 'use strict';
  1513. var
  1514. min = Math.min,
  1515. round = Math.round,
  1516. getCanvas = function () { return document.createElement('canvas'); },
  1517. support = false,
  1518. exifOrientation = {
  1519. 8: 270
  1520. , 3: 180
  1521. , 6: 90
  1522. , 7: 270
  1523. , 4: 180
  1524. , 5: 90
  1525. }
  1526. ;
  1527. try {
  1528. support = getCanvas().toDataURL('image/png').indexOf('data:image/png') > -1;
  1529. }
  1530. catch (e){}
  1531. function Image(file){
  1532. if( file instanceof Image ){
  1533. var img = new Image(file.file);
  1534. api.extend(img.matrix, file.matrix);
  1535. return img;
  1536. }
  1537. else if( !(this instanceof Image) ){
  1538. return new Image(file);
  1539. }
  1540. this.file = file;
  1541. this.size = file.size || 100;
  1542. this.matrix = {
  1543. sx: 0,
  1544. sy: 0,
  1545. sw: 0,
  1546. sh: 0,
  1547. dx: 0,
  1548. dy: 0,
  1549. dw: 0,
  1550. dh: 0,
  1551. resize: 0, // min, max OR preview
  1552. deg: 0,
  1553. quality: 1, // jpeg quality
  1554. filter: 0
  1555. };
  1556. }
  1557. Image.prototype = {
  1558. image: true,
  1559. constructor: Image,
  1560. set: function (attrs){
  1561. api.extend(this.matrix, attrs);
  1562. return this;
  1563. },
  1564. crop: function (x, y, w, h){
  1565. if( w === undef ){
  1566. w = x;
  1567. h = y;
  1568. x = y = 0;
  1569. }
  1570. return this.set({ sx: x, sy: y, sw: w, sh: h || w });
  1571. },
  1572. resize: function (w, h, strategy){
  1573. if( /min|max/.test(h) ){
  1574. strategy = h;
  1575. h = w;
  1576. }
  1577. return this.set({ dw: w, dh: h || w, resize: strategy });
  1578. },
  1579. preview: function (w, h){
  1580. return this.resize(w, h || w, 'preview');
  1581. },
  1582. rotate: function (deg){
  1583. return this.set({ deg: deg });
  1584. },
  1585. filter: function (filter){
  1586. return this.set({ filter: filter });
  1587. },
  1588. overlay: function (images){
  1589. return this.set({ overlay: images });
  1590. },
  1591. clone: function (){
  1592. return new Image(this);
  1593. },
  1594. _load: function (image, fn){
  1595. var self = this;
  1596. if( /img|video/i.test(image.nodeName) ){
  1597. fn.call(self, null, image);
  1598. }
  1599. else {
  1600. api.readAsImage(image, function (evt){
  1601. fn.call(self, evt.type != 'load', evt.result);
  1602. });
  1603. }
  1604. },
  1605. _apply: function (image, fn){
  1606. var
  1607. canvas = getCanvas()
  1608. , m = this.getMatrix(image)
  1609. , ctx = canvas.getContext('2d')
  1610. , width = image.videoWidth || image.width
  1611. , height = image.videoHeight || image.height
  1612. , deg = m.deg
  1613. , dw = m.dw
  1614. , dh = m.dh
  1615. , w = width
  1616. , h = height
  1617. , filter = m.filter
  1618. , copy // canvas copy
  1619. , buffer = image
  1620. , overlay = m.overlay
  1621. , queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); })
  1622. , renderImageToCanvas = api.renderImageToCanvas
  1623. ;
  1624. // Normalize angle
  1625. deg = deg - Math.floor(deg/360)*360;
  1626. // For `renderImageToCanvas`
  1627. image._type = this.file.type;
  1628. while(m.multipass && min(w/dw, h/dh) > 2 ){
  1629. w = (w/2 + 0.5)|0;
  1630. h = (h/2 + 0.5)|0;
  1631. copy = getCanvas();
  1632. copy.width = w;
  1633. copy.height = h;
  1634. if( buffer !== image ){
  1635. renderImageToCanvas(copy, buffer, 0, 0, buffer.width, buffer.height, 0, 0, w, h);
  1636. buffer = copy;
  1637. }
  1638. else {
  1639. buffer = copy;
  1640. renderImageToCanvas(buffer, image, m.sx, m.sy, m.sw, m.sh, 0, 0, w, h);
  1641. m.sx = m.sy = m.sw = m.sh = 0;
  1642. }
  1643. }
  1644. canvas.width = (deg % 180) ? dh : dw;
  1645. canvas.height = (deg % 180) ? dw : dh;
  1646. canvas.type = m.type;
  1647. canvas.quality = m.quality;
  1648. ctx.rotate(deg * Math.PI / 180);
  1649. renderImageToCanvas(ctx.canvas, buffer
  1650. , m.sx, m.sy
  1651. , m.sw || buffer.width
  1652. , m.sh || buffer.height
  1653. , (deg == 180 || deg == 270 ? -dw : 0)
  1654. , (deg == 90 || deg == 180 ? -dh : 0)
  1655. , dw, dh
  1656. );
  1657. dw = canvas.width;
  1658. dh = canvas.height;
  1659. // Apply overlay
  1660. overlay && api.each([].concat(overlay), function (over){
  1661. queue.inc();
  1662. // preload
  1663. var img = new window.Image, fn = function (){
  1664. var
  1665. x = over.x|0
  1666. , y = over.y|0
  1667. , w = over.w || img.width
  1668. , h = over.h || img.height
  1669. , rel = over.rel
  1670. ;
  1671. // center | right | left
  1672. x = (rel == 1 || rel == 4 || rel == 7) ? (dw - w + x)/2 : (rel == 2 || rel == 5 || rel == 8 ? dw - (w + x) : x);
  1673. // center | bottom | top
  1674. y = (rel == 3 || rel == 4 || rel == 5) ? (dh - h + y)/2 : (rel >= 6 ? dh - (h + y) : y);
  1675. api.event.off(img, 'error load abort', fn);
  1676. try {
  1677. ctx.globalAlpha = over.opacity || 1;
  1678. ctx.drawImage(img, x, y, w, h);
  1679. }
  1680. catch (er){}
  1681. queue.next();
  1682. };
  1683. api.event.on(img, 'error load abort', fn);
  1684. img.src = over.src;
  1685. if( img.complete ){
  1686. fn();
  1687. }
  1688. });
  1689. if( filter ){
  1690. queue.inc();
  1691. Image.applyFilter(canvas, filter, queue.next);
  1692. }
  1693. queue.check();
  1694. },
  1695. getMatrix: function (image){
  1696. var
  1697. m = api.extend({}, this.matrix)
  1698. , sw = m.sw = m.sw || image.videoWidth || image.naturalWidth || image.width
  1699. , sh = m.sh = m.sh || image.videoHeight || image.naturalHeight || image.height
  1700. , dw = m.dw = m.dw || sw
  1701. , dh = m.dh = m.dh || sh
  1702. , sf = sw/sh, df = dw/dh
  1703. , strategy = m.resize
  1704. ;
  1705. if( strategy == 'preview' ){
  1706. if( dw != sw || dh != sh ){
  1707. // Make preview
  1708. var w, h;
  1709. if( df >= sf ){
  1710. w = sw;
  1711. h = w / df;
  1712. } else {
  1713. h = sh;
  1714. w = h * df;
  1715. }
  1716. if( w != sw || h != sh ){
  1717. m.sx = ~~((sw - w)/2);
  1718. m.sy = ~~((sh - h)/2);
  1719. sw = w;
  1720. sh = h;
  1721. }
  1722. }
  1723. }
  1724. else if( strategy ){
  1725. if( !(sw > dw || sh > dh) ){
  1726. dw = sw;
  1727. dh = sh;
  1728. }
  1729. else if( strategy == 'min' ){
  1730. dw = round(sf < df ? min(sw, dw) : dh*sf);
  1731. dh = round(sf < df ? dw/sf : min(sh, dh));
  1732. }
  1733. else {
  1734. dw = round(sf >= df ? min(sw, dw) : dh*sf);
  1735. dh = round(sf >= df ? dw/sf : min(sh, dh));
  1736. }
  1737. }
  1738. m.sw = sw;
  1739. m.sh = sh;
  1740. m.dw = dw;
  1741. m.dh = dh;
  1742. m.multipass = api.multiPassResize;
  1743. return m;
  1744. },
  1745. _trans: function (fn){
  1746. this._load(this.file, function (err, image){
  1747. if( err ){
  1748. fn(err);
  1749. }
  1750. else {
  1751. try {
  1752. this._apply(image, fn);
  1753. } catch (err){
  1754. api.log('[err] FileAPI.Image.fn._apply:', err);
  1755. fn(err);
  1756. }
  1757. }
  1758. });
  1759. },
  1760. get: function (fn){
  1761. if( api.support.transform ){
  1762. var _this = this, matrix = _this.matrix;
  1763. if( matrix.deg == 'auto' ){
  1764. api.getInfo(_this.file, function (err, info){
  1765. // rotate by exif orientation
  1766. matrix.deg = exifOrientation[info && info.exif && info.exif.Orientation] || 0;
  1767. _this._trans(fn);
  1768. });
  1769. }
  1770. else {
  1771. _this._trans(fn);
  1772. }
  1773. }
  1774. else {
  1775. fn('not_support_transform');
  1776. }
  1777. return this;
  1778. },
  1779. toData: function (fn){
  1780. return this.get(fn);
  1781. }
  1782. };
  1783. Image.exifOrientation = exifOrientation;
  1784. Image.transform = function (file, transform, autoOrientation, fn){
  1785. function _transform(err, img){
  1786. // img -- info object
  1787. var
  1788. images = {}
  1789. , queue = api.queue(function (err){
  1790. fn(err, images);
  1791. })
  1792. ;
  1793. if( !err ){
  1794. api.each(transform, function (params, name){
  1795. if( !queue.isFail() ){
  1796. var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function';
  1797. if( isFn ){
  1798. params(img, ImgTrans);
  1799. }
  1800. else if( params.width ){
  1801. ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy);
  1802. }
  1803. else {
  1804. if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){
  1805. ImgTrans.resize(params.maxWidth, params.maxHeight, 'max');
  1806. }
  1807. }
  1808. if( params.crop ){
  1809. var crop = params.crop;
  1810. ImgTrans.crop(crop.x|0, crop.y|0, crop.w || crop.width, crop.h || crop.height);
  1811. }
  1812. if( params.rotate === undef && autoOrientation ){
  1813. params.rotate = 'auto';
  1814. }
  1815. ImgTrans.set({ type: ImgTrans.matrix.type || params.type || file.type || 'image/png' });
  1816. if( !isFn ){
  1817. ImgTrans.set({
  1818. deg: params.rotate
  1819. , overlay: params.overlay
  1820. , filter: params.filter
  1821. , quality: params.quality || 1
  1822. });
  1823. }
  1824. queue.inc();
  1825. ImgTrans.toData(function (err, image){
  1826. if( err ){
  1827. queue.fail();
  1828. }
  1829. else {
  1830. images[name] = image;
  1831. queue.next();
  1832. }
  1833. });
  1834. }
  1835. });
  1836. }
  1837. else {
  1838. queue.fail();
  1839. }
  1840. }
  1841. // @todo: Оло-ло, нужно рефакторить это место
  1842. if( file.width ){
  1843. _transform(false, file);
  1844. } else {
  1845. api.getInfo(file, _transform);
  1846. }
  1847. };
  1848. // @const
  1849. api.each(['TOP', 'CENTER', 'BOTTOM'], function (x, i){
  1850. api.each(['LEFT', 'CENTER', 'RIGHT'], function (y, j){
  1851. Image[x+'_'+y] = i*3 + j;
  1852. Image[y+'_'+x] = i*3 + j;
  1853. });
  1854. });
  1855. /**
  1856. * Trabsform element to canvas
  1857. *
  1858. * @param {Image|HTMLVideoElement} el
  1859. * @returns {Canvas}
  1860. */
  1861. Image.toCanvas = function(el){
  1862. var canvas = document.createElement('canvas');
  1863. canvas.width = el.videoWidth || el.width;
  1864. canvas.height = el.videoHeight || el.height;
  1865. canvas.getContext('2d').drawImage(el, 0, 0);
  1866. return canvas;
  1867. };
  1868. /**
  1869. * Create image from DataURL
  1870. * @param {String} dataURL
  1871. * @param {Object} size
  1872. * @param {Function} callback
  1873. */
  1874. Image.fromDataURL = function (dataURL, size, callback){
  1875. var img = api.newImage(dataURL);
  1876. api.extend(img, size);
  1877. callback(img);
  1878. };
  1879. /**
  1880. * Apply filter (caman.js)
  1881. *
  1882. * @param {Canvas|Image} canvas
  1883. * @param {String|Function} filter
  1884. * @param {Function} doneFn
  1885. */
  1886. Image.applyFilter = function (canvas, filter, doneFn){
  1887. if( typeof filter == 'function' ){
  1888. filter(canvas, doneFn);
  1889. }
  1890. else if( window.Caman ){
  1891. // http://camanjs.com/guides/
  1892. window.Caman(canvas.tagName == 'IMG' ? Image.toCanvas(canvas) : canvas, function (){
  1893. if( typeof filter == 'string' ){
  1894. this[filter]();
  1895. }
  1896. else {
  1897. api.each(filter, function (val, method){
  1898. this[method](val);
  1899. }, this);
  1900. }
  1901. this.render(doneFn);
  1902. });
  1903. }
  1904. };
  1905. /**
  1906. * For load-image-ios.js
  1907. */
  1908. api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){
  1909. try {
  1910. return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
  1911. } catch (ex) {
  1912. api.log('renderImageToCanvas failed');
  1913. throw ex;
  1914. }
  1915. };
  1916. // @export
  1917. api.support.canvas = api.support.transform = support;
  1918. api.Image = Image;
  1919. })(FileAPI, document);
  1920. /*
  1921. * JavaScript Load Image iOS scaling fixes 1.0.3
  1922. * https://github.com/blueimp/JavaScript-Load-Image
  1923. *
  1924. * Copyright 2013, Sebastian Tschan
  1925. * https://blueimp.net
  1926. *
  1927. * iOS image scaling fixes based on
  1928. * https://github.com/stomita/ios-imagefile-megapixel
  1929. *
  1930. * Licensed under the MIT license:
  1931. * http://www.opensource.org/licenses/MIT
  1932. */
  1933. /*jslint nomen: true, bitwise: true */
  1934. /*global FileAPI, window, document */
  1935. (function (factory) {
  1936. 'use strict';
  1937. factory(FileAPI);
  1938. }(function (loadImage) {
  1939. 'use strict';
  1940. // Only apply fixes on the iOS platform:
  1941. if (!window.navigator || !window.navigator.platform ||
  1942. !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
  1943. return;
  1944. }
  1945. var originalRenderMethod = loadImage.renderImageToCanvas;
  1946. // Detects subsampling in JPEG images:
  1947. loadImage.detectSubsampling = function (img) {
  1948. var canvas,
  1949. context;
  1950. if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
  1951. canvas = document.createElement('canvas');
  1952. canvas.width = canvas.height = 1;
  1953. context = canvas.getContext('2d');
  1954. context.drawImage(img, -img.width + 1, 0);
  1955. // subsampled image becomes half smaller in rendering size.
  1956. // check alpha channel value to confirm image is covering edge pixel or not.
  1957. // if alpha value is 0 image is not covering, hence subsampled.
  1958. return context.getImageData(0, 0, 1, 1).data[3] === 0;
  1959. }
  1960. return false;
  1961. };
  1962. // Detects vertical squash in JPEG images:
  1963. loadImage.detectVerticalSquash = function (img, subsampled) {
  1964. var naturalHeight = img.naturalHeight || img.height,
  1965. canvas = document.createElement('canvas'),
  1966. context = canvas.getContext('2d'),
  1967. data,
  1968. sy,
  1969. ey,
  1970. py,
  1971. alpha;
  1972. if (subsampled) {
  1973. naturalHeight /= 2;
  1974. }
  1975. canvas.width = 1;
  1976. canvas.height = naturalHeight;
  1977. context.drawImage(img, 0, 0);
  1978. data = context.getImageData(0, 0, 1, naturalHeight).data;
  1979. // search image edge pixel position in case it is squashed vertically:
  1980. sy = 0;
  1981. ey = naturalHeight;
  1982. py = naturalHeight;
  1983. while (py > sy) {
  1984. alpha = data[(py - 1) * 4 + 3];
  1985. if (alpha === 0) {
  1986. ey = py;
  1987. } else {
  1988. sy = py;
  1989. }
  1990. py = (ey + sy) >> 1;
  1991. }
  1992. return (py / naturalHeight) || 1;
  1993. };
  1994. // Renders image to canvas while working around iOS image scaling bugs:
  1995. // https://github.com/blueimp/JavaScript-Load-Image/issues/13
  1996. loadImage.renderImageToCanvas = function (
  1997. canvas,
  1998. img,
  1999. sourceX,
  2000. sourceY,
  2001. sourceWidth,
  2002. sourceHeight,
  2003. destX,
  2004. destY,
  2005. destWidth,
  2006. destHeight
  2007. ) {
  2008. if (img._type === 'image/jpeg') {
  2009. var context = canvas.getContext('2d'),
  2010. tmpCanvas = document.createElement('canvas'),
  2011. tileSize = 1024,
  2012. tmpContext = tmpCanvas.getContext('2d'),
  2013. subsampled,
  2014. vertSquashRatio,
  2015. tileX,
  2016. tileY;
  2017. tmpCanvas.width = tileSize;
  2018. tmpCanvas.height = tileSize;
  2019. context.save();
  2020. subsampled = loadImage.detectSubsampling(img);
  2021. if (subsampled) {
  2022. sourceX /= 2;
  2023. sourceY /= 2;
  2024. sourceWidth /= 2;
  2025. sourceHeight /= 2;
  2026. }
  2027. vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
  2028. if (subsampled || vertSquashRatio !== 1) {
  2029. sourceY *= vertSquashRatio;
  2030. destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
  2031. destHeight = Math.ceil(
  2032. tileSize * destHeight / sourceHeight / vertSquashRatio
  2033. );
  2034. destY = 0;
  2035. tileY = 0;
  2036. while (tileY < sourceHeight) {
  2037. destX = 0;
  2038. tileX = 0;
  2039. while (tileX < sourceWidth) {
  2040. tmpContext.clearRect(0, 0, tileSize, tileSize);
  2041. tmpContext.drawImage(
  2042. img,
  2043. sourceX,
  2044. sourceY,
  2045. sourceWidth,
  2046. sourceHeight,
  2047. -tileX,
  2048. -tileY,
  2049. sourceWidth,
  2050. sourceHeight
  2051. );
  2052. context.drawImage(
  2053. tmpCanvas,
  2054. 0,
  2055. 0,
  2056. tileSize,
  2057. tileSize,
  2058. destX,
  2059. destY,
  2060. destWidth,
  2061. destHeight
  2062. );
  2063. tileX += tileSize;
  2064. destX += destWidth;
  2065. }
  2066. tileY += tileSize;
  2067. destY += destHeight;
  2068. }
  2069. context.restore();
  2070. return canvas;
  2071. }
  2072. }
  2073. return originalRenderMethod(
  2074. canvas,
  2075. img,
  2076. sourceX,
  2077. sourceY,
  2078. sourceWidth,
  2079. sourceHeight,
  2080. destX,
  2081. destY,
  2082. destWidth,
  2083. destHeight
  2084. );
  2085. };
  2086. }));
  2087. /*global window, FileAPI */
  2088. (function (api, window){
  2089. "use strict";
  2090. var
  2091. document = window.document
  2092. , FormData = window.FormData
  2093. , Form = function (){ this.items = []; }
  2094. , encodeURIComponent = window.encodeURIComponent
  2095. ;
  2096. Form.prototype = {
  2097. append: function (name, blob, file, type){
  2098. this.items.push({
  2099. name: name
  2100. , blob: blob && blob.blob || (blob == void 0 ? '' : blob)
  2101. , file: blob && (file || blob.name)
  2102. , type: blob && (type || blob.type)
  2103. });
  2104. },
  2105. each: function (fn){
  2106. var i = 0, n = this.items.length;
  2107. for( ; i < n; i++ ){
  2108. fn.call(this, this.items[i]);
  2109. }
  2110. },
  2111. toData: function (fn, options){
  2112. // allow chunked transfer if we have only one file to send
  2113. // flag is used below and in XHR._send
  2114. options._chunked = api.support.chunked && options.chunkSize > 0 && api.filter(this.items, function (item){ return item.file; }).length == 1;
  2115. if( !api.support.html5 ){
  2116. api.log('FileAPI.Form.toHtmlData');
  2117. this.toHtmlData(fn);
  2118. }
  2119. else if( !api.formData || this.multipart || !FormData ){
  2120. api.log('FileAPI.Form.toMultipartData');
  2121. this.toMultipartData(fn);
  2122. }
  2123. else if( options._chunked ){
  2124. api.log('FileAPI.Form.toPlainData');
  2125. this.toPlainData(fn);
  2126. }
  2127. else {
  2128. api.log('FileAPI.Form.toFormData');
  2129. this.toFormData(fn);
  2130. }
  2131. },
  2132. _to: function (data, complete, next, arg){
  2133. var queue = api.queue(function (){
  2134. complete(data);
  2135. });
  2136. this.each(function (file){
  2137. next(file, data, queue, arg);
  2138. });
  2139. queue.check();
  2140. },
  2141. toHtmlData: function (fn){
  2142. this._to(document.createDocumentFragment(), fn, function (file, data/**DocumentFragment*/){
  2143. var blob = file.blob, hidden;
  2144. if( file.file ){
  2145. api.reset(blob, true);
  2146. // set new name
  2147. blob.name = file.name;
  2148. blob.disabled = false;
  2149. data.appendChild(blob);
  2150. }
  2151. else {
  2152. hidden = document.createElement('input');
  2153. hidden.name = file.name;
  2154. hidden.type = 'hidden';
  2155. hidden.value = blob;
  2156. data.appendChild(hidden);
  2157. }
  2158. });
  2159. },
  2160. toPlainData: function (fn){
  2161. this._to({}, fn, function (file, data, queue){
  2162. if( file.file ){
  2163. data.type = file.file;
  2164. }
  2165. if( file.blob.toBlob ){
  2166. // canvas
  2167. queue.inc();
  2168. _convertFile(file, function (file, blob){
  2169. data.name = file.name;
  2170. data.file = blob;
  2171. data.size = blob.length;
  2172. data.type = file.type;
  2173. queue.next();
  2174. });
  2175. }
  2176. else if( file.file ){
  2177. // file
  2178. data.name = file.blob.name;
  2179. data.file = file.blob;
  2180. data.size = file.blob.size;
  2181. data.type = file.type;
  2182. }
  2183. else {
  2184. // additional data
  2185. if( !data.params ){
  2186. data.params = [];
  2187. }
  2188. data.params.push(encodeURIComponent(file.name) +"="+ encodeURIComponent(file.blob));
  2189. }
  2190. data.start = -1;
  2191. data.end = data.file && data.file.FileAPIReadPosition || -1;
  2192. data.retry = 0;
  2193. });
  2194. },
  2195. toFormData: function (fn){
  2196. this._to(new FormData, fn, function (file, data, queue){
  2197. if( file.blob && file.blob.toBlob ){
  2198. queue.inc();
  2199. _convertFile(file, function (file, blob){
  2200. data.append(file.name, blob, file.file);
  2201. queue.next();
  2202. });
  2203. }
  2204. else if( file.file ){
  2205. data.append(file.name, file.blob, file.file);
  2206. }
  2207. else {
  2208. data.append(file.name, file.blob);
  2209. }
  2210. if( file.file ){
  2211. data.append('_'+file.name, file.file);
  2212. }
  2213. });
  2214. },
  2215. toMultipartData: function (fn){
  2216. this._to([], fn, function (file, data, queue, boundary){
  2217. queue.inc();
  2218. _convertFile(file, function (file, blob){
  2219. data.push(
  2220. '--_' + boundary + ('\r\nContent-Disposition: form-data; name="'+ file.name +'"'+ (file.file ? '; filename="'+ encodeURIComponent(file.file) +'"' : '')
  2221. + (file.file ? '\r\nContent-Type: '+ (file.type || 'application/octet-stream') : '')
  2222. + '\r\n'
  2223. + '\r\n'+ (file.file ? blob : encodeURIComponent(blob))
  2224. + '\r\n')
  2225. );
  2226. queue.next();
  2227. }, true);
  2228. }, api.expando);
  2229. }
  2230. };
  2231. function _convertFile(file, fn, useBinaryString){
  2232. var blob = file.blob, filename = file.file;
  2233. if( filename ){
  2234. if( !blob.toDataURL ){
  2235. // The Blob is not an image.
  2236. api.readAsBinaryString(blob, function (evt){
  2237. if( evt.type == 'load' ){
  2238. fn(file, evt.result);
  2239. }
  2240. });
  2241. return;
  2242. }
  2243. var
  2244. mime = { 'image/jpeg': '.jpe?g', 'image/png': '.png' }
  2245. , type = mime[file.type] ? file.type : 'image/png'
  2246. , ext = mime[type] || '.png'
  2247. , quality = blob.quality || 1
  2248. ;
  2249. if( !filename.match(new RegExp(ext+'$', 'i')) ){
  2250. // Does not change the current extension, but add a new one.
  2251. filename += ext.replace('?', '');
  2252. }
  2253. file.file = filename;
  2254. file.type = type;
  2255. if( !useBinaryString && blob.toBlob ){
  2256. blob.toBlob(function (blob){
  2257. fn(file, blob);
  2258. }, type, quality);
  2259. }
  2260. else {
  2261. fn(file, api.toBinaryString(blob.toDataURL(type, quality)));
  2262. }
  2263. }
  2264. else {
  2265. fn(file, blob);
  2266. }
  2267. }
  2268. // @export
  2269. api.Form = Form;
  2270. })(FileAPI, window);
  2271. /*global window, FileAPI, Uint8Array */
  2272. (function (window, api){
  2273. "use strict";
  2274. var
  2275. noop = function (){}
  2276. , document = window.document
  2277. , XHR = function (options){
  2278. this.uid = api.uid();
  2279. this.xhr = {
  2280. abort: noop
  2281. , getResponseHeader: noop
  2282. , getAllResponseHeaders: noop
  2283. };
  2284. this.options = options;
  2285. },
  2286. _xhrResponsePostfix = { '': 1, XML: 1, Text: 1, Body: 1 }
  2287. ;
  2288. XHR.prototype = {
  2289. status: 0,
  2290. statusText: '',
  2291. constructor: XHR,
  2292. getResponseHeader: function (name){
  2293. return this.xhr.getResponseHeader(name);
  2294. },
  2295. getAllResponseHeaders: function (){
  2296. return this.xhr.getAllResponseHeaders() || {};
  2297. },
  2298. end: function (status, statusText){
  2299. var _this = this, options = _this.options;
  2300. _this.end =
  2301. _this.abort = noop;
  2302. _this.status = status;
  2303. if( statusText ){
  2304. _this.statusText = statusText;
  2305. }
  2306. api.log('xhr.end:', status, statusText);
  2307. options.complete(status == 200 || status == 201 ? false : _this.statusText || 'unknown', _this);
  2308. if( _this.xhr && _this.xhr.node ){
  2309. setTimeout(function (){
  2310. var node = _this.xhr.node;
  2311. try { node.parentNode.removeChild(node); } catch (e){}
  2312. try { delete window[_this.uid]; } catch (e){}
  2313. window[_this.uid] = _this.xhr.node = null;
  2314. }, 9);
  2315. }
  2316. },
  2317. abort: function (){
  2318. this.end(0, 'abort');
  2319. if( this.xhr ){
  2320. this.xhr.aborted = true;
  2321. this.xhr.abort();
  2322. }
  2323. },
  2324. send: function (FormData){
  2325. var _this = this, options = this.options;
  2326. FormData.toData(function (data){
  2327. // Start uploading
  2328. options.upload(options, _this);
  2329. _this._send.call(_this, options, data);
  2330. }, options);
  2331. },
  2332. _send: function (options, data){
  2333. var _this = this, xhr, uid = _this.uid, onloadFuncName = _this.uid + "Load", url = options.url;
  2334. api.log('XHR._send:', data);
  2335. if( !options.cache ){
  2336. // No cache
  2337. url += (~url.indexOf('?') ? '&' : '?') + api.uid();
  2338. }
  2339. if( data.nodeName ){
  2340. var jsonp = options.jsonp;
  2341. // prepare callback in GET
  2342. url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid);
  2343. // legacy
  2344. options.upload(options, _this);
  2345. var
  2346. onPostMessage = function (evt){
  2347. if( ~url.indexOf(evt.origin) ){
  2348. try {
  2349. var result = api.parseJSON(evt.data);
  2350. if( result.id == uid ){
  2351. complete(result.status, result.statusText, result.response);
  2352. }
  2353. } catch( err ){
  2354. complete(0, err.message);
  2355. }
  2356. }
  2357. },
  2358. // jsonp-callack
  2359. complete = window[uid] = function (status, statusText, response){
  2360. _this.readyState = 4;
  2361. _this.responseText = response;
  2362. _this.end(status, statusText);
  2363. api.event.off(window, 'message', onPostMessage);
  2364. window[uid] = xhr = transport = window[onloadFuncName] = null;
  2365. }
  2366. ;
  2367. _this.xhr.abort = function (){
  2368. try {
  2369. if( transport.stop ){ transport.stop(); }
  2370. else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
  2371. else { transport.contentWindow.document.execCommand('Stop'); }
  2372. }
  2373. catch (er) {}
  2374. complete(0, "abort");
  2375. };
  2376. api.event.on(window, 'message', onPostMessage);
  2377. window[onloadFuncName] = function (){
  2378. try {
  2379. var
  2380. win = transport.contentWindow
  2381. , doc = win.document
  2382. , result = win.result || api.parseJSON(doc.body.innerHTML)
  2383. ;
  2384. complete(result.status, result.statusText, result.response);
  2385. } catch (e){
  2386. api.log('[transport.onload]', e);
  2387. }
  2388. };
  2389. xhr = document.createElement('div');
  2390. xhr.innerHTML = '<form target="'+ uid +'" action="'+ url +'" method="POST" enctype="multipart/form-data" style="position: absolute; top: -1000px; overflow: hidden; width: 1px; height: 1px;">'
  2391. + '<iframe name="'+ uid +'" src="javascript:false;" onload="' + onloadFuncName + '()"></iframe>'
  2392. + (jsonp && (options.url.indexOf('=?') < 0) ? '<input value="'+ uid +'" name="'+jsonp+'" type="hidden"/>' : '')
  2393. + '</form>'
  2394. ;
  2395. // get form-data & transport
  2396. var
  2397. form = xhr.getElementsByTagName('form')[0]
  2398. , transport = xhr.getElementsByTagName('iframe')[0]
  2399. ;
  2400. form.appendChild(data);
  2401. api.log(form.parentNode.innerHTML);
  2402. // append to DOM
  2403. document.body.appendChild(xhr);
  2404. // keep a reference to node-transport
  2405. _this.xhr.node = xhr;
  2406. // send
  2407. _this.readyState = 2; // loaded
  2408. form.submit();
  2409. form = null;
  2410. }
  2411. else {
  2412. // Clean url
  2413. url = url.replace(/([a-z]+)=(\?)&?/i, '');
  2414. // html5
  2415. if (this.xhr && this.xhr.aborted) {
  2416. api.log("Error: already aborted");
  2417. return;
  2418. }
  2419. xhr = _this.xhr = api.getXHR();
  2420. if (data.params) {
  2421. url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
  2422. }
  2423. xhr.open('POST', url, true);
  2424. if( api.withCredentials ){
  2425. xhr.withCredentials = "true";
  2426. }
  2427. if( !options.headers || !options.headers['X-Requested-With'] ){
  2428. xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  2429. }
  2430. api.each(options.headers, function (val, key){
  2431. xhr.setRequestHeader(key, val);
  2432. });
  2433. if ( options._chunked ) {
  2434. // chunked upload
  2435. if( xhr.upload ){
  2436. xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
  2437. if (!data.retry) {
  2438. // show progress only for correct chunk uploads
  2439. options.progress({
  2440. type: evt.type
  2441. , total: data.size
  2442. , loaded: data.start + evt.loaded
  2443. , totalSize: data.size
  2444. }, _this, options);
  2445. }
  2446. }, 100), false);
  2447. }
  2448. xhr.onreadystatechange = function (){
  2449. var lkb = parseInt(xhr.getResponseHeader('X-Last-Known-Byte'), 10);
  2450. _this.status = xhr.status;
  2451. _this.statusText = xhr.statusText;
  2452. _this.readyState = xhr.readyState;
  2453. if( xhr.readyState == 4 ){
  2454. try {
  2455. for( var k in _xhrResponsePostfix ){
  2456. _this['response'+k] = xhr['response'+k];
  2457. }
  2458. }catch(_){}
  2459. xhr.onreadystatechange = null;
  2460. if (!xhr.status || xhr.status - 201 > 0) {
  2461. api.log("Error: " + xhr.status);
  2462. // some kind of error
  2463. // 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action
  2464. // up - server error
  2465. if (((!xhr.status && !xhr.aborted) || 500 == xhr.status || 416 == xhr.status) && ++data.retry <= options.chunkUploadRetry) {
  2466. // let's try again the same chunk
  2467. // only applicable for recoverable error codes 500 && 416
  2468. var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout;
  2469. // inform about recoverable problems
  2470. options.pause(data.file, options);
  2471. // smart restart if server reports about the last known byte
  2472. api.log("X-Last-Known-Byte: " + lkb);
  2473. if (lkb) {
  2474. data.end = lkb;
  2475. } else {
  2476. data.end = data.start - 1;
  2477. if (416 == xhr.status) {
  2478. data.end = data.end - options.chunkSize;
  2479. }
  2480. }
  2481. setTimeout(function () {
  2482. _this._send(options, data);
  2483. }, delay);
  2484. } else {
  2485. // no mo retries
  2486. _this.end(xhr.status);
  2487. }
  2488. } else {
  2489. // success
  2490. data.retry = 0;
  2491. if (data.end == data.size - 1) {
  2492. // finished
  2493. _this.end(xhr.status);
  2494. } else {
  2495. // next chunk
  2496. // shift position if server reports about the last known byte
  2497. api.log("X-Last-Known-Byte: " + lkb);
  2498. if (lkb) {
  2499. data.end = lkb;
  2500. }
  2501. data.file.FileAPIReadPosition = data.end;
  2502. setTimeout(function () {
  2503. _this._send(options, data);
  2504. }, 0);
  2505. }
  2506. }
  2507. xhr = null;
  2508. }
  2509. };
  2510. data.start = data.end + 1;
  2511. data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start);
  2512. // Retrieve a slice of file
  2513. var
  2514. file = data.file
  2515. , slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1)
  2516. ;
  2517. if( data.size && !slice.size ){
  2518. setTimeout(function (){
  2519. _this.end(-1);
  2520. });
  2521. } else {
  2522. xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
  2523. xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
  2524. xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
  2525. xhr.send(slice);
  2526. }
  2527. file = slice = null;
  2528. } else {
  2529. // single piece upload
  2530. if( xhr.upload ){
  2531. // https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29
  2532. xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
  2533. options.progress(evt, _this, options);
  2534. }, 100), false);
  2535. }
  2536. xhr.onreadystatechange = function (){
  2537. _this.status = xhr.status;
  2538. _this.statusText = xhr.statusText;
  2539. _this.readyState = xhr.readyState;
  2540. if( xhr.readyState == 4 ){
  2541. for( var k in _xhrResponsePostfix ){
  2542. _this['response'+k] = xhr['response'+k];
  2543. }
  2544. xhr.onreadystatechange = null;
  2545. if (!xhr.status || xhr.status > 201) {
  2546. api.log("Error: " + xhr.status);
  2547. if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) {
  2548. options.retry = (options.retry || 0) + 1;
  2549. var delay = api.networkDownRetryTimeout;
  2550. // inform about recoverable problems
  2551. options.pause(options.file, options);
  2552. setTimeout(function () {
  2553. _this._send(options, data);
  2554. }, delay);
  2555. } else {
  2556. //success
  2557. _this.end(xhr.status);
  2558. }
  2559. } else {
  2560. //success
  2561. _this.end(xhr.status);
  2562. }
  2563. xhr = null;
  2564. }
  2565. };
  2566. if( api.isArray(data) ){
  2567. // multipart
  2568. xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
  2569. var rawData = data.join('') +'--_'+ api.expando +'--';
  2570. /** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
  2571. if( xhr.sendAsBinary ){
  2572. xhr.sendAsBinary(rawData);
  2573. }
  2574. else {
  2575. var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; });
  2576. xhr.send(new Uint8Array(bytes).buffer);
  2577. }
  2578. } else {
  2579. // FormData
  2580. xhr.send(data);
  2581. }
  2582. }
  2583. }
  2584. }
  2585. };
  2586. // @export
  2587. api.XHR = XHR;
  2588. })(window, FileAPI);
  2589. /**
  2590. * @class FileAPI.Camera
  2591. * @author RubaXa <trash@rubaxa.org>
  2592. * @support Chrome 21+, FF 18+, Opera 12+
  2593. */
  2594. /*global window, FileAPI, jQuery */
  2595. /** @namespace LocalMediaStream -- https://developer.mozilla.org/en-US/docs/WebRTC/MediaStream_API#LocalMediaStream */
  2596. (function (window, api){
  2597. "use strict";
  2598. var
  2599. URL = window.URL || window.webkitURL,
  2600. document = window.document,
  2601. navigator = window.navigator,
  2602. getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia,
  2603. html5 = !!getMedia
  2604. ;
  2605. // Support "media"
  2606. api.support.media = html5;
  2607. var Camera = function (video){
  2608. this.video = video;
  2609. };
  2610. Camera.prototype = {
  2611. isActive: function (){
  2612. return !!this._active;
  2613. },
  2614. /**
  2615. * Start camera streaming
  2616. * @param {Function} callback
  2617. */
  2618. start: function (callback){
  2619. var
  2620. _this = this
  2621. , video = _this.video
  2622. , _successId
  2623. , _failId
  2624. , _complete = function (err){
  2625. _this._active = !err;
  2626. clearTimeout(_failId);
  2627. clearTimeout(_successId);
  2628. // api.event.off(video, 'loadedmetadata', _complete);
  2629. callback && callback(err, _this);
  2630. }
  2631. ;
  2632. getMedia.call(navigator, { video: true }, function (stream/**LocalMediaStream*/){
  2633. // Success
  2634. _this.stream = stream;
  2635. // api.event.on(video, 'loadedmetadata', function (){
  2636. // _complete(null);
  2637. // });
  2638. // Set camera stream
  2639. video.src = URL.createObjectURL(stream);
  2640. // Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
  2641. // See crbug.com/110938.
  2642. _successId = setInterval(function (){
  2643. if( _detectVideoSignal(video) ){
  2644. _complete(null);
  2645. }
  2646. }, 1000);
  2647. _failId = setTimeout(function (){
  2648. _complete('timeout');
  2649. }, 5000);
  2650. // Go-go-go!
  2651. video.play();
  2652. }, _complete/*error*/);
  2653. },
  2654. /**
  2655. * Stop camera streaming
  2656. */
  2657. stop: function (){
  2658. try {
  2659. this._active = false;
  2660. this.video.pause();
  2661. this.stream.stop();
  2662. } catch( err ){ }
  2663. },
  2664. /**
  2665. * Create screenshot
  2666. * @return {FileAPI.Camera.Shot}
  2667. */
  2668. shot: function (){
  2669. return new Shot(this.video);
  2670. }
  2671. };
  2672. /**
  2673. * Get camera element from container
  2674. *
  2675. * @static
  2676. * @param {HTMLElement} el
  2677. * @return {Camera}
  2678. */
  2679. Camera.get = function (el){
  2680. return new Camera(el.firstChild);
  2681. };
  2682. /**
  2683. * Publish camera element into container
  2684. *
  2685. * @static
  2686. * @param {HTMLElement} el
  2687. * @param {Object} options
  2688. * @param {Function} [callback]
  2689. */
  2690. Camera.publish = function (el, options, callback){
  2691. if( typeof options == 'function' ){
  2692. callback = options;
  2693. options = {};
  2694. }
  2695. // Dimensions of "camera"
  2696. options = api.extend({}, {
  2697. width: '100%'
  2698. , height: '100%'
  2699. , start: true
  2700. }, options);
  2701. if( el.jquery ){
  2702. // Extract first element, from jQuery collection
  2703. el = el[0];
  2704. }
  2705. var doneFn = function (err){
  2706. if( err ){
  2707. callback(err);
  2708. }
  2709. else {
  2710. // Get camera
  2711. var cam = Camera.get(el);
  2712. if( options.start ){
  2713. cam.start(callback);
  2714. }
  2715. else {
  2716. callback(null, cam);
  2717. }
  2718. }
  2719. };
  2720. el.style.width = _px(options.width);
  2721. el.style.height = _px(options.height);
  2722. if( api.html5 && html5 ){
  2723. // Create video element
  2724. var video = document.createElement('video');
  2725. // Set dimensions
  2726. video.style.width = _px(options.width);
  2727. video.style.height = _px(options.height);
  2728. // Clean container
  2729. if( window.jQuery ){
  2730. jQuery(el).empty();
  2731. } else {
  2732. el.innerHTML = '';
  2733. }
  2734. // Add "camera" to container
  2735. el.appendChild(video);
  2736. // end
  2737. doneFn();
  2738. }
  2739. else {
  2740. Camera.fallback(el, options, doneFn);
  2741. }
  2742. };
  2743. Camera.fallback = function (el, options, callback){
  2744. callback('not_support_camera');
  2745. };
  2746. /**
  2747. * @class FileAPI.Camera.Shot
  2748. */
  2749. var Shot = function (video){
  2750. var canvas = video.nodeName ? api.Image.toCanvas(video) : video;
  2751. var shot = api.Image(canvas);
  2752. shot.type = 'image/png';
  2753. shot.width = canvas.width;
  2754. shot.height = canvas.height;
  2755. shot.size = canvas.width * canvas.height * 4;
  2756. return shot;
  2757. };
  2758. /**
  2759. * Add "px" postfix, if value is a number
  2760. *
  2761. * @private
  2762. * @param {*} val
  2763. * @return {String}
  2764. */
  2765. function _px(val){
  2766. return val >= 0 ? val + 'px' : val;
  2767. }
  2768. /**
  2769. * @private
  2770. * @param {HTMLVideoElement} video
  2771. * @return {Boolean}
  2772. */
  2773. function _detectVideoSignal(video){
  2774. var canvas = document.createElement('canvas'), ctx, res = false;
  2775. try {
  2776. ctx = canvas.getContext('2d');
  2777. ctx.drawImage(video, 0, 0, 1, 1);
  2778. res = ctx.getImageData(0, 0, 1, 1).data[4] != 255;
  2779. }
  2780. catch( e ){}
  2781. return res;
  2782. }
  2783. // @export
  2784. Camera.Shot = Shot;
  2785. api.Camera = Camera;
  2786. })(window, FileAPI);
  2787. /**
  2788. * FileAPI fallback to Flash
  2789. *
  2790. * @flash-developer "Vladimir Demidov" <v.demidov@corp.mail.ru>
  2791. */
  2792. /*global window, ActiveXObject, FileAPI */
  2793. (function (window, jQuery, api) {
  2794. "use strict";
  2795. var
  2796. document = window.document
  2797. , location = window.location
  2798. , navigator = window.navigator
  2799. , _each = api.each
  2800. ;
  2801. api.support.flash = (function (){
  2802. var mime = navigator.mimeTypes, has = false;
  2803. if( navigator.plugins && typeof navigator.plugins['Shockwave Flash'] == 'object' ){
  2804. has = navigator.plugins['Shockwave Flash'].description && !(mime && mime['application/x-shockwave-flash'] && !mime['application/x-shockwave-flash'].enabledPlugin);
  2805. }
  2806. else {
  2807. try {
  2808. has = !!(window.ActiveXObject && new ActiveXObject('ShockwaveFlash.ShockwaveFlash'));
  2809. }
  2810. catch(er){
  2811. api.log('Flash -- does not supported.');
  2812. }
  2813. }
  2814. if( has && /^file:/i.test(location) ){
  2815. api.log('[warn] Flash does not work on `file:` protocol.');
  2816. }
  2817. return has;
  2818. })();
  2819. api.support.flash
  2820. && (0
  2821. || !api.html5 || !api.support.html5
  2822. || (api.cors && !api.support.cors)
  2823. || (api.media && !api.support.media)
  2824. )
  2825. && (function (){
  2826. var
  2827. _attr = api.uid()
  2828. , _retry = 0
  2829. , _files = {}
  2830. , _rhttp = /^https?:/i
  2831. , flash = {
  2832. _fn: {},
  2833. /**
  2834. * Publish flash-object
  2835. *
  2836. * @param {HTMLElement} el
  2837. * @param {String} id
  2838. * @param {Object} [opts]
  2839. */
  2840. publish: function (el, id, opts){
  2841. opts = opts || {};
  2842. el.innerHTML = _makeFlashHTML({
  2843. id: id
  2844. , src: _getUrl(api.flashUrl, 'r=' + api.version)
  2845. // , src: _getUrl('http://v.demidov.boom.corp.mail.ru/uploaderfileapi/FlashFileAPI.swf?1')
  2846. , wmode: opts.camera ? '' : 'transparent'
  2847. , flashvars: 'callback=' + (opts.onEvent || 'FileAPI.Flash.onEvent')
  2848. + '&flashId='+ id
  2849. + '&storeKey='+ navigator.userAgent.match(/\d/ig).join('') +'_'+ api.version
  2850. + (flash.isReady || (api.pingUrl ? '&ping='+api.pingUrl : ''))
  2851. + '&timeout='+api.flashAbortTimeout
  2852. + (opts.camera ? '&useCamera=' + _getUrl(api.flashWebcamUrl) : '')
  2853. + '&debug='+(api.debug?"1":"")
  2854. }, opts);
  2855. },
  2856. /**
  2857. * Initialization & preload flash object
  2858. */
  2859. init: function (){
  2860. var child = document.body && document.body.firstChild;
  2861. if( child ){
  2862. do {
  2863. if( child.nodeType == 1 ){
  2864. api.log('FlashAPI.state: awaiting');
  2865. var dummy = document.createElement('div');
  2866. dummy.id = '_' + _attr;
  2867. _css(dummy, {
  2868. top: 1
  2869. , right: 1
  2870. , width: 5
  2871. , height: 5
  2872. , position: 'absolute'
  2873. , zIndex: 1e6+'' // set max zIndex
  2874. });
  2875. child.parentNode.insertBefore(dummy, child);
  2876. flash.publish(dummy, _attr);
  2877. return;
  2878. }
  2879. }
  2880. while( child = child.nextSibling );
  2881. }
  2882. if( _retry < 10 ){
  2883. setTimeout(flash.init, ++_retry*50);
  2884. }
  2885. },
  2886. ready: function (){
  2887. api.log('FlashAPI.state: ready');
  2888. flash.ready = api.F;
  2889. flash.isReady = true;
  2890. flash.patch();
  2891. flash.patchCamera && flash.patchCamera();
  2892. api.event.on(document, 'mouseover', flash.mouseover);
  2893. api.event.on(document, 'click', function (evt){
  2894. if( flash.mouseover(evt) ){
  2895. evt.preventDefault
  2896. ? evt.preventDefault()
  2897. : (evt.returnValue = true)
  2898. ;
  2899. }
  2900. });
  2901. },
  2902. getEl: function (){
  2903. return document.getElementById('_'+_attr);
  2904. },
  2905. getWrapper: function (node){
  2906. do {
  2907. if( /js-fileapi-wrapper/.test(node.className) ){
  2908. return node;
  2909. }
  2910. }
  2911. while( (node = node.parentNode) && (node !== document.body) );
  2912. },
  2913. disableMouseover: false,
  2914. mouseover: function (evt){
  2915. if (!flash.disableMouseover) {
  2916. var target = api.event.fix(evt).target;
  2917. if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){
  2918. var
  2919. state = target.getAttribute(_attr)
  2920. , wrapper = flash.getWrapper(target)
  2921. ;
  2922. if( api.multiFlash ){
  2923. // check state:
  2924. // i — published
  2925. // i — initialization
  2926. // r — ready
  2927. if( state == 'i' || state == 'r' ){
  2928. // publish fail
  2929. return false;
  2930. }
  2931. else if( state != 'p' ){
  2932. // set "init" state
  2933. target.setAttribute(_attr, 'i');
  2934. var dummy = document.createElement('div');
  2935. if( !wrapper ){
  2936. api.log('[err] FlashAPI.mouseover: js-fileapi-wrapper not found');
  2937. return;
  2938. }
  2939. _css(dummy, {
  2940. top: 0
  2941. , left: 0
  2942. , width: target.offsetWidth
  2943. , height: target.offsetHeight
  2944. , zIndex: 1e6+'' // set max zIndex
  2945. , position: 'absolute'
  2946. });
  2947. wrapper.appendChild(dummy);
  2948. flash.publish(dummy, api.uid());
  2949. // set "publish" state
  2950. target.setAttribute(_attr, 'p');
  2951. }
  2952. return true;
  2953. }
  2954. else if( wrapper ){
  2955. // Use one flash element
  2956. var box = _getDimensions(wrapper);
  2957. _css(flash.getEl(), box);
  2958. // Set current input
  2959. flash.curInp = target;
  2960. }
  2961. }
  2962. else if( !/object|embed/i.test(target.nodeName) ){
  2963. _css(flash.getEl(), { top: 1, left: 1, width: 5, height: 5 });
  2964. }
  2965. }
  2966. },
  2967. onEvent: function (evt){
  2968. var type = evt.type;
  2969. if( type == 'ready' ){
  2970. try {
  2971. // set "ready" state
  2972. flash.getInput(evt.flashId).setAttribute(_attr, 'r');
  2973. } catch (e){
  2974. }
  2975. flash.ready();
  2976. setTimeout(function (){ flash.mouseenter(evt); }, 50);
  2977. return true;
  2978. }
  2979. else if( type === 'ping' ){
  2980. api.log('(flash -> js).ping:', [evt.status, evt.savedStatus], evt.error);
  2981. }
  2982. else if( type === 'log' ){
  2983. api.log('(flash -> js).log:', evt.target);
  2984. }
  2985. else if( type in flash ){
  2986. setTimeout(function (){
  2987. api.log('FlashAPI.event.'+evt.type+':', evt);
  2988. flash[type](evt);
  2989. }, 1);
  2990. }
  2991. },
  2992. mouseDown: function(evt) {
  2993. flash.disableMouseover = true;
  2994. },
  2995. cancel: function(evt) {
  2996. flash.disableMouseover = false;
  2997. },
  2998. mouseenter: function (evt){
  2999. var node = flash.getInput(evt.flashId);
  3000. if( node ){
  3001. // Set multiple mode
  3002. flash.cmd(evt, 'multiple', node.getAttribute('multiple') != null);
  3003. // Set files filter
  3004. var accept = [], exts = {};
  3005. _each((node.getAttribute('accept') || '').split(/,\s*/), function (mime){
  3006. api.accept[mime] && _each(api.accept[mime].split(' '), function (ext){
  3007. exts[ext] = 1;
  3008. });
  3009. });
  3010. _each(exts, function (i, ext){
  3011. accept.push( ext );
  3012. });
  3013. flash.cmd(evt, 'accept', accept.length ? accept.join(',')+','+accept.join(',').toUpperCase() : '*');
  3014. }
  3015. },
  3016. get: function (id){
  3017. return document[id] || window[id] || document.embeds[id];
  3018. },
  3019. getInput: function (id){
  3020. if( api.multiFlash ){
  3021. try {
  3022. var node = flash.getWrapper(flash.get(id));
  3023. if( node ){
  3024. return node.getElementsByTagName('input')[0];
  3025. }
  3026. } catch (e){
  3027. api.log('[err] Can not find "input" by flashId:', id, e);
  3028. }
  3029. } else {
  3030. return flash.curInp;
  3031. }
  3032. },
  3033. select: function (evt){
  3034. try {
  3035. var
  3036. inp = flash.getInput(evt.flashId)
  3037. , uid = api.uid(inp)
  3038. , files = evt.target.files
  3039. , event
  3040. ;
  3041. _each(files, function (file){
  3042. api.checkFileObj(file);
  3043. });
  3044. _files[uid] = files;
  3045. if( document.createEvent ){
  3046. event = document.createEvent('Event');
  3047. event.files = files;
  3048. event.initEvent('change', true, true);
  3049. inp.dispatchEvent(event);
  3050. }
  3051. else if( jQuery ){
  3052. jQuery(inp).trigger({ type: 'change', files: files });
  3053. }
  3054. else {
  3055. event = document.createEventObject();
  3056. event.files = files;
  3057. inp.fireEvent('onchange', event);
  3058. }
  3059. } finally {
  3060. flash.disableMouseover = false;
  3061. }
  3062. },
  3063. interval: null,
  3064. cmd: function (id, name, data, last) {
  3065. if (flash.uploadInProgress && flash.readInProgress) {
  3066. setTimeout(function() {
  3067. flash.cmd(id, name, data, last);
  3068. }, 100);
  3069. } else {
  3070. this.cmdFn(id, name, data, last);
  3071. }
  3072. },
  3073. cmdFn: function(id, name, data, last) {
  3074. try {
  3075. api.log('(js -> flash).'+name+':', data);
  3076. return flash.get(id.flashId || id).cmd(name, data);
  3077. } catch (e){
  3078. api.log('(js -> flash).onError:', e);
  3079. if( !last ){
  3080. // try again
  3081. setTimeout(function (){ flash.cmd(id, name, data, true); }, 50);
  3082. }
  3083. }
  3084. },
  3085. patch: function (){
  3086. api.flashEngine = true;
  3087. // FileAPI
  3088. _inherit(api, {
  3089. readAsDataURL: function (file, callback){
  3090. if( _isHtmlFile(file) ){
  3091. this.parent.apply(this, arguments);
  3092. }
  3093. else {
  3094. api.log('FlashAPI.readAsBase64');
  3095. flash.readInProgress = true;
  3096. flash.cmd(file, 'readAsBase64', {
  3097. id: file.id,
  3098. callback: _wrap(function _(err, base64){
  3099. flash.readInProgress = false;
  3100. _unwrap(_);
  3101. api.log('FlashAPI.readAsBase64:', err);
  3102. callback({
  3103. type: err ? 'error' : 'load'
  3104. , error: err
  3105. , result: 'data:'+ file.type +';base64,'+ base64
  3106. });
  3107. })
  3108. });
  3109. }
  3110. },
  3111. readAsText: function (file, encoding, callback){
  3112. if( callback ){
  3113. api.log('[warn] FlashAPI.readAsText not supported `encoding` param');
  3114. } else {
  3115. callback = encoding;
  3116. }
  3117. api.readAsDataURL(file, function (evt){
  3118. if( evt.type == 'load' ){
  3119. try {
  3120. evt.result = window.atob(evt.result.split(';base64,')[1]);
  3121. } catch( err ){
  3122. evt.type = 'error';
  3123. evt.error = err.toString();
  3124. }
  3125. }
  3126. callback(evt);
  3127. });
  3128. },
  3129. getFiles: function (input, filter, callback){
  3130. if( callback ){
  3131. api.filterFiles(api.getFiles(input), filter, callback);
  3132. return null;
  3133. }
  3134. var files = api.isArray(input) ? input : _files[api.uid(input.target || input.srcElement || input)];
  3135. if( !files ){
  3136. // Файлов нету, вызываем родительский метод
  3137. return this.parent.apply(this, arguments);
  3138. }
  3139. if( filter ){
  3140. filter = api.getFilesFilter(filter);
  3141. files = api.filter(files, function (file){ return filter.test(file.name); });
  3142. }
  3143. return files;
  3144. },
  3145. getInfo: function (file, fn){
  3146. if( _isHtmlFile(file) ){
  3147. this.parent.apply(this, arguments);
  3148. }
  3149. else if( file.isShot ){
  3150. fn(null, file.info = {
  3151. width: file.width,
  3152. height: file.height
  3153. });
  3154. }
  3155. else {
  3156. if( !file.__info ){
  3157. var defer = file.__info = api.defer();
  3158. // flash.cmd(file, 'getFileInfo', {
  3159. // id: file.id
  3160. // , callback: _wrap(function _(err, info){
  3161. // _unwrap(_);
  3162. // defer.resolve(err, file.info = info);
  3163. // })
  3164. // });
  3165. defer.resolve(null, file.info = null);
  3166. }
  3167. file.__info.then(fn);
  3168. }
  3169. }
  3170. });
  3171. // FileAPI.Image
  3172. api.support.transform = true;
  3173. api.Image && _inherit(api.Image.prototype, {
  3174. get: function (fn, scaleMode){
  3175. this.set({ scaleMode: scaleMode || 'noScale' }); // noScale, exactFit
  3176. return this.parent(fn);
  3177. },
  3178. _load: function (file, fn){
  3179. api.log('FlashAPI.Image._load:', file);
  3180. if( _isHtmlFile(file) ){
  3181. this.parent.apply(this, arguments);
  3182. }
  3183. else {
  3184. var _this = this;
  3185. api.getInfo(file, function (err){
  3186. fn.call(_this, err, file);
  3187. });
  3188. }
  3189. },
  3190. _apply: function (file, fn){
  3191. api.log('FlashAPI.Image._apply:', file);
  3192. if( _isHtmlFile(file) ){
  3193. this.parent.apply(this, arguments);
  3194. }
  3195. else {
  3196. var m = this.getMatrix(file.info), doneFn = fn;
  3197. flash.cmd(file, 'imageTransform', {
  3198. id: file.id
  3199. , matrix: m
  3200. , callback: _wrap(function _(err, base64){
  3201. api.log('FlashAPI.Image._apply.callback:', err);
  3202. _unwrap(_);
  3203. if( err ){
  3204. doneFn(err);
  3205. }
  3206. else if( !api.support.html5 && (!api.support.dataURI || base64.length > 3e4) ){
  3207. _makeFlashImage({
  3208. width: (m.deg % 180) ? m.dh : m.dw
  3209. , height: (m.deg % 180) ? m.dw : m.dh
  3210. , scale: m.scaleMode
  3211. }, base64, doneFn);
  3212. }
  3213. else {
  3214. if( m.filter ){
  3215. doneFn = function (err, img){
  3216. if( err ){
  3217. fn(err);
  3218. }
  3219. else {
  3220. api.Image.applyFilter(img, m.filter, function (){
  3221. fn(err, this.canvas);
  3222. });
  3223. }
  3224. };
  3225. }
  3226. api.newImage('data:'+ file.type +';base64,'+ base64, doneFn);
  3227. }
  3228. })
  3229. });
  3230. }
  3231. },
  3232. toData: function (fn){
  3233. var
  3234. file = this.file
  3235. , info = file.info
  3236. , matrix = this.getMatrix(info)
  3237. ;
  3238. api.log('FlashAPI.Image.toData');
  3239. if( _isHtmlFile(file) ){
  3240. this.parent.apply(this, arguments);
  3241. }
  3242. else {
  3243. if( matrix.deg == 'auto' ){
  3244. matrix.deg = api.Image.exifOrientation[info && info.exif && info.exif.Orientation] || 0;
  3245. }
  3246. fn.call(this, !file.info, {
  3247. id: file.id
  3248. , flashId: file.flashId
  3249. , name: file.name
  3250. , type: file.type
  3251. , matrix: matrix
  3252. });
  3253. }
  3254. }
  3255. });
  3256. api.Image && _inherit(api.Image, {
  3257. fromDataURL: function (dataURL, size, callback){
  3258. if( !api.support.dataURI || dataURL.length > 3e4 ){
  3259. _makeFlashImage(
  3260. api.extend({ scale: 'exactFit' }, size)
  3261. , dataURL.replace(/^data:[^,]+,/, '')
  3262. , function (err, el){ callback(el); }
  3263. );
  3264. }
  3265. else {
  3266. this.parent(dataURL, size, callback);
  3267. }
  3268. }
  3269. });
  3270. // FileAPI.Form
  3271. _inherit(api.Form.prototype, {
  3272. toData: function (fn){
  3273. var items = this.items, i = items.length;
  3274. for( ; i--; ){
  3275. if( items[i].file && _isHtmlFile(items[i].blob) ){
  3276. return this.parent.apply(this, arguments);
  3277. }
  3278. }
  3279. api.log('FlashAPI.Form.toData');
  3280. fn(items);
  3281. }
  3282. });
  3283. // FileAPI.XHR
  3284. _inherit(api.XHR.prototype, {
  3285. _send: function (options, formData){
  3286. if(
  3287. formData.nodeName
  3288. || formData.append && api.support.html5
  3289. || api.isArray(formData) && (typeof formData[0] === 'string')
  3290. ){
  3291. // HTML5, Multipart or IFrame
  3292. return this.parent.apply(this, arguments);
  3293. }
  3294. var
  3295. data = {}
  3296. , files = {}
  3297. , _this = this
  3298. , flashId
  3299. , fileId
  3300. ;
  3301. _each(formData, function (item){
  3302. if( item.file ){
  3303. files[item.name] = item = _getFileDescr(item.blob);
  3304. fileId = item.id;
  3305. flashId = item.flashId;
  3306. }
  3307. else {
  3308. data[item.name] = item.blob;
  3309. }
  3310. });
  3311. if( !fileId ){
  3312. flashId = _attr;
  3313. }
  3314. if( !flashId ){
  3315. api.log('[err] FlashAPI._send: flashId -- undefined');
  3316. return this.parent.apply(this, arguments);
  3317. }
  3318. else {
  3319. api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId);
  3320. }
  3321. _this.xhr = {
  3322. headers: {},
  3323. abort: function (){ flash.uploadInProgress = false; flash.cmd(flashId, 'abort', { id: fileId }); },
  3324. getResponseHeader: function (name){ return this.headers[name]; },
  3325. getAllResponseHeaders: function (){ return this.headers; }
  3326. };
  3327. var queue = api.queue(function (){
  3328. flash.uploadInProgress = true;
  3329. flash.cmd(flashId, 'upload', {
  3330. url: _getUrl(options.url.replace(/([a-z]+)=(\?)&?/i, ''))
  3331. , data: data
  3332. , files: fileId ? files : null
  3333. , headers: options.headers || {}
  3334. , callback: _wrap(function upload(evt){
  3335. var type = evt.type, result = evt.result;
  3336. api.log('FlashAPI.upload.'+type);
  3337. if( type == 'progress' ){
  3338. evt.loaded = Math.min(evt.loaded, evt.total); // @todo fixme
  3339. evt.lengthComputable = true;
  3340. options.progress(evt);
  3341. }
  3342. else if( type == 'complete' ){
  3343. flash.uploadInProgress = false;
  3344. _unwrap(upload);
  3345. if( typeof result == 'string' ){
  3346. _this.responseText = result.replace(/%22/g, "\"").replace(/%5c/g, "\\").replace(/%26/g, "&").replace(/%25/g, "%");
  3347. }
  3348. _this.end(evt.status || 200);
  3349. }
  3350. else if( type == 'abort' || type == 'error' ){
  3351. flash.uploadInProgress = false;
  3352. _this.end(evt.status || 0, evt.message);
  3353. _unwrap(upload);
  3354. }
  3355. })
  3356. });
  3357. });
  3358. // #2174: FileReference.load() call while FileReference.upload() or vice versa
  3359. _each(files, function (file){
  3360. queue.inc();
  3361. api.getInfo(file, queue.next);
  3362. });
  3363. queue.check();
  3364. }
  3365. });
  3366. }
  3367. }
  3368. ;
  3369. function _makeFlashHTML(opts){
  3370. return ('<object id="#id#" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+(opts.width || '100%')+'" height="'+(opts.height || '100%')+'">'
  3371. + '<param name="movie" value="#src#" />'
  3372. + '<param name="flashvars" value="#flashvars#" />'
  3373. + '<param name="swliveconnect" value="true" />'
  3374. + '<param name="allowscriptaccess" value="always" />'
  3375. + '<param name="allownetworking" value="all" />'
  3376. + '<param name="menu" value="false" />'
  3377. + '<param name="wmode" value="#wmode#" />'
  3378. + '<embed flashvars="#flashvars#" swliveconnect="true" allownetworking="all" allowscriptaccess="always" name="#id#" src="#src#" width="'+(opts.width || '100%')+'" height="'+(opts.height || '100%')+'" menu="false" wmode="transparent" type="application/x-shockwave-flash"></embed>'
  3379. + '</object>').replace(/#(\w+)#/ig, function (a, name){ return opts[name]; })
  3380. ;
  3381. }
  3382. function _css(el, css){
  3383. if( el && el.style ){
  3384. var key, val;
  3385. for( key in css ){
  3386. val = css[key];
  3387. if( typeof val == 'number' ){
  3388. val += 'px';
  3389. }
  3390. try { el.style[key] = val; } catch (e) {}
  3391. }
  3392. }
  3393. }
  3394. function _inherit(obj, methods){
  3395. _each(methods, function (fn, name){
  3396. var prev = obj[name];
  3397. obj[name] = function (){
  3398. this.parent = prev;
  3399. return fn.apply(this, arguments);
  3400. };
  3401. });
  3402. }
  3403. function _isHtmlFile(file){
  3404. return file && !file.flashId;
  3405. }
  3406. function _wrap(fn){
  3407. var id = fn.wid = api.uid();
  3408. flash._fn[id] = fn;
  3409. return 'FileAPI.Flash._fn.'+id;
  3410. }
  3411. function _unwrap(fn){
  3412. try {
  3413. flash._fn[fn.wid] = null;
  3414. delete flash._fn[fn.wid];
  3415. }
  3416. catch(e){}
  3417. }
  3418. function _getUrl(url, params){
  3419. if( !_rhttp.test(url) ){
  3420. if( /^\.\//.test(url) || '/' != url.charAt(0) ){
  3421. var path = location.pathname;
  3422. path = path.substr(0, path.lastIndexOf('/'));
  3423. url = (path +'/'+ url).replace('/./', '/');
  3424. }
  3425. if( '//' != url.substr(0, 2) ){
  3426. url = '//' + location.host + url;
  3427. }
  3428. if( !_rhttp.test(url) ){
  3429. url = location.protocol + url;
  3430. }
  3431. }
  3432. if( params ){
  3433. url += (/\?/.test(url) ? '&' : '?') + params;
  3434. }
  3435. return url;
  3436. }
  3437. function _makeFlashImage(opts, base64, fn){
  3438. var
  3439. key
  3440. , flashId = api.uid()
  3441. , el = document.createElement('div')
  3442. , attempts = 10
  3443. ;
  3444. for( key in opts ){
  3445. el.setAttribute(key, opts[key]);
  3446. el[key] = opts[key];
  3447. }
  3448. _css(el, opts);
  3449. opts.width = '100%';
  3450. opts.height = '100%';
  3451. el.innerHTML = _makeFlashHTML(api.extend({
  3452. id: flashId
  3453. , src: _getUrl(api.flashImageUrl, 'r='+ api.uid())
  3454. , wmode: 'opaque'
  3455. , flashvars: 'scale='+ opts.scale +'&callback='+_wrap(function _(){
  3456. _unwrap(_);
  3457. if( --attempts > 0 ){
  3458. _setImage();
  3459. }
  3460. return true;
  3461. })
  3462. }, opts));
  3463. function _setImage(){
  3464. try {
  3465. // Get flash-object by id
  3466. var img = flash.get(flashId);
  3467. img.setImage(base64);
  3468. } catch (e){
  3469. api.log('[err] FlashAPI.Preview.setImage -- can not set "base64":', e);
  3470. }
  3471. }
  3472. fn(false, el);
  3473. el = null;
  3474. }
  3475. function _getFileDescr(file){
  3476. return {
  3477. id: file.id
  3478. , name: file.name
  3479. , matrix: file.matrix
  3480. , flashId: file.flashId
  3481. };
  3482. }
  3483. function _getDimensions(el){
  3484. var
  3485. box = el.getBoundingClientRect()
  3486. , body = document.body
  3487. , docEl = (el && el.ownerDocument).documentElement
  3488. ;
  3489. function getOffset(obj) {
  3490. var left, top;
  3491. left = top = 0;
  3492. if (obj.offsetParent) {
  3493. do {
  3494. left += obj.offsetLeft;
  3495. top += obj.offsetTop;
  3496. } while (obj = obj.offsetParent);
  3497. }
  3498. return {
  3499. left : left,
  3500. top : top
  3501. };
  3502. };
  3503. return {
  3504. top: getOffset(el).top
  3505. , left: getOffset(el).left
  3506. , width: el.offsetWidth
  3507. , height: el.offsetHeight
  3508. };
  3509. }
  3510. // @export
  3511. api.Flash = flash;
  3512. // Check dataURI support
  3513. api.newImage('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', function (err, img){
  3514. api.support.dataURI = !(img.width != 1 || img.height != 1);
  3515. flash.init();
  3516. });
  3517. })();
  3518. })(window, window.jQuery, FileAPI);
  3519. /**
  3520. * FileAPI fallback to Flash
  3521. *
  3522. * @flash-developer "Vladimir Demidov" <v.demidov@corp.mail.ru>
  3523. */
  3524. /*global window, FileAPI */
  3525. (function (window, jQuery, api) {
  3526. "use strict";
  3527. var _each = api.each,
  3528. _cameraQueue = [];
  3529. if (api.support.flash && (api.media && !api.support.media)) {
  3530. (function () {
  3531. function _wrap(fn) {
  3532. var id = fn.wid = api.uid();
  3533. api.Flash._fn[id] = fn;
  3534. return 'FileAPI.Flash._fn.' + id;
  3535. }
  3536. function _unwrap(fn) {
  3537. try {
  3538. api.Flash._fn[fn.wid] = null;
  3539. delete api.Flash._fn[fn.wid];
  3540. } catch (e) {
  3541. }
  3542. }
  3543. var flash = api.Flash;
  3544. api.extend(api.Flash, {
  3545. patchCamera: function () {
  3546. api.Camera.fallback = function (el, options, callback) {
  3547. var camId = api.uid();
  3548. api.log('FlashAPI.Camera.publish: ' + camId);
  3549. flash.publish(el, camId, api.extend(options, {
  3550. camera: true,
  3551. onEvent: _wrap(function _(evt) {
  3552. if (evt.type === 'camera') {
  3553. _unwrap(_);
  3554. if (evt.error) {
  3555. api.log('FlashAPI.Camera.publish.error: ' + evt.error);
  3556. callback(evt.error);
  3557. } else {
  3558. api.log('FlashAPI.Camera.publish.success: ' + camId);
  3559. callback(null);
  3560. }
  3561. }
  3562. })
  3563. }));
  3564. };
  3565. // Run
  3566. _each(_cameraQueue, function (args) {
  3567. api.Camera.fallback.apply(api.Camera, args);
  3568. });
  3569. _cameraQueue = [];
  3570. // FileAPI.Camera:proto
  3571. api.extend(api.Camera.prototype, {
  3572. _id: function () {
  3573. return this.video.id;
  3574. },
  3575. start: function (callback) {
  3576. var _this = this;
  3577. flash.cmd(this._id(), 'camera.on', {
  3578. callback: _wrap(function _(evt) {
  3579. _unwrap(_);
  3580. if (evt.error) {
  3581. api.log('FlashAPI.camera.on.error: ' + evt.error);
  3582. callback(evt.error, _this);
  3583. } else {
  3584. api.log('FlashAPI.camera.on.success: ' + _this._id());
  3585. _this._active = true;
  3586. callback(null, _this);
  3587. }
  3588. })
  3589. });
  3590. },
  3591. stop: function () {
  3592. this._active = false;
  3593. flash.cmd(this._id(), 'camera.off');
  3594. },
  3595. shot: function () {
  3596. api.log('FlashAPI.Camera.shot:', this._id());
  3597. var shot = api.Flash.cmd(this._id(), 'shot', {});
  3598. shot.type = 'image/png';
  3599. shot.flashId = this._id();
  3600. shot.isShot = true;
  3601. return new api.Camera.Shot(shot);
  3602. }
  3603. });
  3604. }
  3605. });
  3606. api.Camera.fallback = function () {
  3607. _cameraQueue.push(arguments);
  3608. };
  3609. }());
  3610. }
  3611. }(window, window.jQuery, FileAPI));
  3612. if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }