FileAPI.js 103 KB

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