moxie.js 279 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158
  1. ;var MXI_DEBUG = true;
  2. /**
  3. * mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill
  4. * v1.3.5
  5. *
  6. * Copyright 2013, Moxiecode Systems AB
  7. * Released under GPL License.
  8. *
  9. * License: http://www.plupload.com/license
  10. * Contributing: http://www.plupload.com/contributing
  11. *
  12. * Date: 2016-05-15
  13. */
  14. /**
  15. * Compiled inline version. (Library mode)
  16. */
  17. /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
  18. /*globals $code */
  19. (function(exports, undefined) {
  20. "use strict";
  21. var modules = {};
  22. function require(ids, callback) {
  23. var module, defs = [];
  24. for (var i = 0; i < ids.length; ++i) {
  25. module = modules[ids[i]] || resolve(ids[i]);
  26. if (!module) {
  27. throw 'module definition dependecy not found: ' + ids[i];
  28. }
  29. defs.push(module);
  30. }
  31. callback.apply(null, defs);
  32. }
  33. function define(id, dependencies, definition) {
  34. if (typeof id !== 'string') {
  35. throw 'invalid module definition, module id must be defined and be a string';
  36. }
  37. if (dependencies === undefined) {
  38. throw 'invalid module definition, dependencies must be specified';
  39. }
  40. if (definition === undefined) {
  41. throw 'invalid module definition, definition function must be specified';
  42. }
  43. require(dependencies, function() {
  44. modules[id] = definition.apply(null, arguments);
  45. });
  46. }
  47. function defined(id) {
  48. return !!modules[id];
  49. }
  50. function resolve(id) {
  51. var target = exports;
  52. var fragments = id.split(/[.\/]/);
  53. for (var fi = 0; fi < fragments.length; ++fi) {
  54. if (!target[fragments[fi]]) {
  55. return;
  56. }
  57. target = target[fragments[fi]];
  58. }
  59. return target;
  60. }
  61. function expose(ids) {
  62. for (var i = 0; i < ids.length; i++) {
  63. var target = exports;
  64. var id = ids[i];
  65. var fragments = id.split(/[.\/]/);
  66. for (var fi = 0; fi < fragments.length - 1; ++fi) {
  67. if (target[fragments[fi]] === undefined) {
  68. target[fragments[fi]] = {};
  69. }
  70. target = target[fragments[fi]];
  71. }
  72. target[fragments[fragments.length - 1]] = modules[id];
  73. }
  74. }
  75. // Included from: src/javascript/core/utils/Basic.js
  76. /**
  77. * Basic.js
  78. *
  79. * Copyright 2013, Moxiecode Systems AB
  80. * Released under GPL License.
  81. *
  82. * License: http://www.plupload.com/license
  83. * Contributing: http://www.plupload.com/contributing
  84. */
  85. define('moxie/core/utils/Basic', [], function() {
  86. /**
  87. Gets the true type of the built-in object (better version of typeof).
  88. @author Angus Croll (http://javascriptweblog.wordpress.com/)
  89. @method typeOf
  90. @for Utils
  91. @static
  92. @param {Object} o Object to check.
  93. @return {String} Object [[Class]]
  94. */
  95. var typeOf = function(o) {
  96. var undef;
  97. if (o === undef) {
  98. return 'undefined';
  99. } else if (o === null) {
  100. return 'null';
  101. } else if (o.nodeType) {
  102. return 'node';
  103. }
  104. // the snippet below is awesome, however it fails to detect null, undefined and arguments types in IE lte 8
  105. return ({}).toString.call(o).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
  106. };
  107. /**
  108. Extends the specified object with another object.
  109. @method extend
  110. @static
  111. @param {Object} target Object to extend.
  112. @param {Object} [obj]* Multiple objects to extend with.
  113. @return {Object} Same as target, the extended object.
  114. */
  115. var extend = function(target) {
  116. var undef;
  117. each(arguments, function(arg, i) {
  118. if (i > 0) {
  119. each(arg, function(value, key) {
  120. if (value !== undef) {
  121. if (typeOf(target[key]) === typeOf(value) && !!~inArray(typeOf(value), ['array', 'object'])) {
  122. extend(target[key], value);
  123. } else {
  124. target[key] = value;
  125. }
  126. }
  127. });
  128. }
  129. });
  130. return target;
  131. };
  132. /**
  133. Executes the callback function for each item in array/object. If you return false in the
  134. callback it will break the loop.
  135. @method each
  136. @static
  137. @param {Object} obj Object to iterate.
  138. @param {function} callback Callback function to execute for each item.
  139. */
  140. var each = function(obj, callback) {
  141. var length, key, i, undef;
  142. if (obj) {
  143. if (typeOf(obj.length) === 'number') { // it might be Array, FileList or even arguments object
  144. // Loop array items
  145. for (i = 0, length = obj.length; i < length; i++) {
  146. if (callback(obj[i], i) === false) {
  147. return;
  148. }
  149. }
  150. } else if (typeOf(obj) === 'object') {
  151. // Loop object items
  152. for (key in obj) {
  153. if (obj.hasOwnProperty(key)) {
  154. if (callback(obj[key], key) === false) {
  155. return;
  156. }
  157. }
  158. }
  159. }
  160. }
  161. };
  162. /**
  163. Checks if object is empty.
  164. @method isEmptyObj
  165. @static
  166. @param {Object} o Object to check.
  167. @return {Boolean}
  168. */
  169. var isEmptyObj = function(obj) {
  170. var prop;
  171. if (!obj || typeOf(obj) !== 'object') {
  172. return true;
  173. }
  174. for (prop in obj) {
  175. return false;
  176. }
  177. return true;
  178. };
  179. /**
  180. Recieve an array of functions (usually async) to call in sequence, each function
  181. receives a callback as first argument that it should call, when it completes. Finally,
  182. after everything is complete, main callback is called. Passing truthy value to the
  183. callback as a first argument will interrupt the sequence and invoke main callback
  184. immediately.
  185. @method inSeries
  186. @static
  187. @param {Array} queue Array of functions to call in sequence
  188. @param {Function} cb Main callback that is called in the end, or in case of error
  189. */
  190. var inSeries = function(queue, cb) {
  191. var i = 0, length = queue.length;
  192. if (typeOf(cb) !== 'function') {
  193. cb = function() {};
  194. }
  195. if (!queue || !queue.length) {
  196. cb();
  197. }
  198. function callNext(i) {
  199. if (typeOf(queue[i]) === 'function') {
  200. queue[i](function(error) {
  201. /*jshint expr:true */
  202. ++i < length && !error ? callNext(i) : cb(error);
  203. });
  204. }
  205. }
  206. callNext(i);
  207. };
  208. /**
  209. Recieve an array of functions (usually async) to call in parallel, each function
  210. receives a callback as first argument that it should call, when it completes. After
  211. everything is complete, main callback is called. Passing truthy value to the
  212. callback as a first argument will interrupt the process and invoke main callback
  213. immediately.
  214. @method inParallel
  215. @static
  216. @param {Array} queue Array of functions to call in sequence
  217. @param {Function} cb Main callback that is called in the end, or in case of error
  218. */
  219. var inParallel = function(queue, cb) {
  220. var count = 0, num = queue.length, cbArgs = new Array(num);
  221. each(queue, function(fn, i) {
  222. fn(function(error) {
  223. if (error) {
  224. return cb(error);
  225. }
  226. var args = [].slice.call(arguments);
  227. args.shift(); // strip error - undefined or not
  228. cbArgs[i] = args;
  229. count++;
  230. if (count === num) {
  231. cbArgs.unshift(null);
  232. cb.apply(this, cbArgs);
  233. }
  234. });
  235. });
  236. };
  237. /**
  238. Find an element in array and return it's index if present, otherwise return -1.
  239. @method inArray
  240. @static
  241. @param {Mixed} needle Element to find
  242. @param {Array} array
  243. @return {Int} Index of the element, or -1 if not found
  244. */
  245. var inArray = function(needle, array) {
  246. if (array) {
  247. if (Array.prototype.indexOf) {
  248. return Array.prototype.indexOf.call(array, needle);
  249. }
  250. for (var i = 0, length = array.length; i < length; i++) {
  251. if (array[i] === needle) {
  252. return i;
  253. }
  254. }
  255. }
  256. return -1;
  257. };
  258. /**
  259. Returns elements of first array if they are not present in second. And false - otherwise.
  260. @private
  261. @method arrayDiff
  262. @param {Array} needles
  263. @param {Array} array
  264. @return {Array|Boolean}
  265. */
  266. var arrayDiff = function(needles, array) {
  267. var diff = [];
  268. if (typeOf(needles) !== 'array') {
  269. needles = [needles];
  270. }
  271. if (typeOf(array) !== 'array') {
  272. array = [array];
  273. }
  274. for (var i in needles) {
  275. if (inArray(needles[i], array) === -1) {
  276. diff.push(needles[i]);
  277. }
  278. }
  279. return diff.length ? diff : false;
  280. };
  281. /**
  282. Find intersection of two arrays.
  283. @private
  284. @method arrayIntersect
  285. @param {Array} array1
  286. @param {Array} array2
  287. @return {Array} Intersection of two arrays or null if there is none
  288. */
  289. var arrayIntersect = function(array1, array2) {
  290. var result = [];
  291. each(array1, function(item) {
  292. if (inArray(item, array2) !== -1) {
  293. result.push(item);
  294. }
  295. });
  296. return result.length ? result : null;
  297. };
  298. /**
  299. Forces anything into an array.
  300. @method toArray
  301. @static
  302. @param {Object} obj Object with length field.
  303. @return {Array} Array object containing all items.
  304. */
  305. var toArray = function(obj) {
  306. var i, arr = [];
  307. for (i = 0; i < obj.length; i++) {
  308. arr[i] = obj[i];
  309. }
  310. return arr;
  311. };
  312. /**
  313. Generates an unique ID. The only way a user would be able to get the same ID is if the two persons
  314. at the same exact millisecond manage to get the same 5 random numbers between 0-65535; it also uses
  315. a counter so each ID is guaranteed to be unique for the given page. It is more probable for the earth
  316. to be hit with an asteroid.
  317. @method guid
  318. @static
  319. @param {String} prefix to prepend (by default 'o' will be prepended).
  320. @method guid
  321. @return {String} Virtually unique id.
  322. */
  323. var guid = (function() {
  324. var counter = 0;
  325. return function(prefix) {
  326. var guid = new Date().getTime().toString(32), i;
  327. for (i = 0; i < 5; i++) {
  328. guid += Math.floor(Math.random() * 65535).toString(32);
  329. }
  330. return (prefix || 'o_') + guid + (counter++).toString(32);
  331. };
  332. }());
  333. /**
  334. Trims white spaces around the string
  335. @method trim
  336. @static
  337. @param {String} str
  338. @return {String}
  339. */
  340. var trim = function(str) {
  341. if (!str) {
  342. return str;
  343. }
  344. return String.prototype.trim ? String.prototype.trim.call(str) : str.toString().replace(/^\s*/, '').replace(/\s*$/, '');
  345. };
  346. /**
  347. Parses the specified size string into a byte value. For example 10kb becomes 10240.
  348. @method parseSizeStr
  349. @static
  350. @param {String/Number} size String to parse or number to just pass through.
  351. @return {Number} Size in bytes.
  352. */
  353. var parseSizeStr = function(size) {
  354. if (typeof(size) !== 'string') {
  355. return size;
  356. }
  357. var muls = {
  358. t: 1099511627776,
  359. g: 1073741824,
  360. m: 1048576,
  361. k: 1024
  362. },
  363. mul;
  364. size = /^([0-9\.]+)([tmgk]?)$/.exec(size.toLowerCase().replace(/[^0-9\.tmkg]/g, ''));
  365. mul = size[2];
  366. size = +size[1];
  367. if (muls.hasOwnProperty(mul)) {
  368. size *= muls[mul];
  369. }
  370. return Math.floor(size);
  371. };
  372. /**
  373. * Pseudo sprintf implementation - simple way to replace tokens with specified values.
  374. *
  375. * @param {String} str String with tokens
  376. * @return {String} String with replaced tokens
  377. */
  378. var sprintf = function(str) {
  379. var args = [].slice.call(arguments, 1);
  380. return str.replace(/%[a-z]/g, function() {
  381. var value = args.shift();
  382. return typeOf(value) !== 'undefined' ? value : '';
  383. });
  384. };
  385. return {
  386. guid: guid,
  387. typeOf: typeOf,
  388. extend: extend,
  389. each: each,
  390. isEmptyObj: isEmptyObj,
  391. inSeries: inSeries,
  392. inParallel: inParallel,
  393. inArray: inArray,
  394. arrayDiff: arrayDiff,
  395. arrayIntersect: arrayIntersect,
  396. toArray: toArray,
  397. trim: trim,
  398. sprintf: sprintf,
  399. parseSizeStr: parseSizeStr
  400. };
  401. });
  402. // Included from: src/javascript/core/utils/Env.js
  403. /**
  404. * Env.js
  405. *
  406. * Copyright 2013, Moxiecode Systems AB
  407. * Released under GPL License.
  408. *
  409. * License: http://www.plupload.com/license
  410. * Contributing: http://www.plupload.com/contributing
  411. */
  412. define("moxie/core/utils/Env", [
  413. "moxie/core/utils/Basic"
  414. ], function(Basic) {
  415. /**
  416. * UAParser.js v0.7.7
  417. * Lightweight JavaScript-based User-Agent string parser
  418. * https://github.com/faisalman/ua-parser-js
  419. *
  420. * Copyright © 2012-2015 Faisal Salman <fyzlman@gmail.com>
  421. * Dual licensed under GPLv2 & MIT
  422. */
  423. var UAParser = (function (undefined) {
  424. //////////////
  425. // Constants
  426. /////////////
  427. var EMPTY = '',
  428. UNKNOWN = '?',
  429. FUNC_TYPE = 'function',
  430. UNDEF_TYPE = 'undefined',
  431. OBJ_TYPE = 'object',
  432. MAJOR = 'major',
  433. MODEL = 'model',
  434. NAME = 'name',
  435. TYPE = 'type',
  436. VENDOR = 'vendor',
  437. VERSION = 'version',
  438. ARCHITECTURE= 'architecture',
  439. CONSOLE = 'console',
  440. MOBILE = 'mobile',
  441. TABLET = 'tablet';
  442. ///////////
  443. // Helper
  444. //////////
  445. var util = {
  446. has : function (str1, str2) {
  447. return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1;
  448. },
  449. lowerize : function (str) {
  450. return str.toLowerCase();
  451. }
  452. };
  453. ///////////////
  454. // Map helper
  455. //////////////
  456. var mapper = {
  457. rgx : function () {
  458. // loop through all regexes maps
  459. for (var result, i = 0, j, k, p, q, matches, match, args = arguments; i < args.length; i += 2) {
  460. var regex = args[i], // even sequence (0,2,4,..)
  461. props = args[i + 1]; // odd sequence (1,3,5,..)
  462. // construct object barebones
  463. if (typeof(result) === UNDEF_TYPE) {
  464. result = {};
  465. for (p in props) {
  466. q = props[p];
  467. if (typeof(q) === OBJ_TYPE) {
  468. result[q[0]] = undefined;
  469. } else {
  470. result[q] = undefined;
  471. }
  472. }
  473. }
  474. // try matching uastring with regexes
  475. for (j = k = 0; j < regex.length; j++) {
  476. matches = regex[j].exec(this.getUA());
  477. if (!!matches) {
  478. for (p = 0; p < props.length; p++) {
  479. match = matches[++k];
  480. q = props[p];
  481. // check if given property is actually array
  482. if (typeof(q) === OBJ_TYPE && q.length > 0) {
  483. if (q.length == 2) {
  484. if (typeof(q[1]) == FUNC_TYPE) {
  485. // assign modified match
  486. result[q[0]] = q[1].call(this, match);
  487. } else {
  488. // assign given value, ignore regex match
  489. result[q[0]] = q[1];
  490. }
  491. } else if (q.length == 3) {
  492. // check whether function or regex
  493. if (typeof(q[1]) === FUNC_TYPE && !(q[1].exec && q[1].test)) {
  494. // call function (usually string mapper)
  495. result[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
  496. } else {
  497. // sanitize match using given regex
  498. result[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
  499. }
  500. } else if (q.length == 4) {
  501. result[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
  502. }
  503. } else {
  504. result[q] = match ? match : undefined;
  505. }
  506. }
  507. break;
  508. }
  509. }
  510. if(!!matches) break; // break the loop immediately if match found
  511. }
  512. return result;
  513. },
  514. str : function (str, map) {
  515. for (var i in map) {
  516. // check if array
  517. if (typeof(map[i]) === OBJ_TYPE && map[i].length > 0) {
  518. for (var j = 0; j < map[i].length; j++) {
  519. if (util.has(map[i][j], str)) {
  520. return (i === UNKNOWN) ? undefined : i;
  521. }
  522. }
  523. } else if (util.has(map[i], str)) {
  524. return (i === UNKNOWN) ? undefined : i;
  525. }
  526. }
  527. return str;
  528. }
  529. };
  530. ///////////////
  531. // String map
  532. //////////////
  533. var maps = {
  534. browser : {
  535. oldsafari : {
  536. major : {
  537. '1' : ['/8', '/1', '/3'],
  538. '2' : '/4',
  539. '?' : '/'
  540. },
  541. version : {
  542. '1.0' : '/8',
  543. '1.2' : '/1',
  544. '1.3' : '/3',
  545. '2.0' : '/412',
  546. '2.0.2' : '/416',
  547. '2.0.3' : '/417',
  548. '2.0.4' : '/419',
  549. '?' : '/'
  550. }
  551. }
  552. },
  553. device : {
  554. sprint : {
  555. model : {
  556. 'Evo Shift 4G' : '7373KT'
  557. },
  558. vendor : {
  559. 'HTC' : 'APA',
  560. 'Sprint' : 'Sprint'
  561. }
  562. }
  563. },
  564. os : {
  565. windows : {
  566. version : {
  567. 'ME' : '4.90',
  568. 'NT 3.11' : 'NT3.51',
  569. 'NT 4.0' : 'NT4.0',
  570. '2000' : 'NT 5.0',
  571. 'XP' : ['NT 5.1', 'NT 5.2'],
  572. 'Vista' : 'NT 6.0',
  573. '7' : 'NT 6.1',
  574. '8' : 'NT 6.2',
  575. '8.1' : 'NT 6.3',
  576. 'RT' : 'ARM'
  577. }
  578. }
  579. }
  580. };
  581. //////////////
  582. // Regex map
  583. /////////////
  584. var regexes = {
  585. browser : [[
  586. // Presto based
  587. /(opera\smini)\/([\w\.-]+)/i, // Opera Mini
  588. /(opera\s[mobiletab]+).+version\/([\w\.-]+)/i, // Opera Mobi/Tablet
  589. /(opera).+version\/([\w\.]+)/i, // Opera > 9.80
  590. /(opera)[\/\s]+([\w\.]+)/i // Opera < 9.80
  591. ], [NAME, VERSION], [
  592. /\s(opr)\/([\w\.]+)/i // Opera Webkit
  593. ], [[NAME, 'Opera'], VERSION], [
  594. // Mixed
  595. /(kindle)\/([\w\.]+)/i, // Kindle
  596. /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]+)*/i,
  597. // Lunascape/Maxthon/Netfront/Jasmine/Blazer
  598. // Trident based
  599. /(avant\s|iemobile|slim|baidu)(?:browser)?[\/\s]?([\w\.]*)/i,
  600. // Avant/IEMobile/SlimBrowser/Baidu
  601. /(?:ms|\()(ie)\s([\w\.]+)/i, // Internet Explorer
  602. // Webkit/KHTML based
  603. /(rekonq)\/([\w\.]+)*/i, // Rekonq
  604. /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi)\/([\w\.-]+)/i
  605. // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron
  606. ], [NAME, VERSION], [
  607. /(trident).+rv[:\s]([\w\.]+).+like\sgecko/i // IE11
  608. ], [[NAME, 'IE'], VERSION], [
  609. /(edge)\/((\d+)?[\w\.]+)/i // Microsoft Edge
  610. ], [NAME, VERSION], [
  611. /(yabrowser)\/([\w\.]+)/i // Yandex
  612. ], [[NAME, 'Yandex'], VERSION], [
  613. /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
  614. ], [[NAME, /_/g, ' '], VERSION], [
  615. /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i,
  616. // Chrome/OmniWeb/Arora/Tizen/Nokia
  617. /(uc\s?browser|qqbrowser)[\/\s]?([\w\.]+)/i
  618. // UCBrowser/QQBrowser
  619. ], [NAME, VERSION], [
  620. /(dolfin)\/([\w\.]+)/i // Dolphin
  621. ], [[NAME, 'Dolphin'], VERSION], [
  622. /((?:android.+)crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
  623. ], [[NAME, 'Chrome'], VERSION], [
  624. /XiaoMi\/MiuiBrowser\/([\w\.]+)/i // MIUI Browser
  625. ], [VERSION, [NAME, 'MIUI Browser']], [
  626. /android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)/i // Android Browser
  627. ], [VERSION, [NAME, 'Android Browser']], [
  628. /FBAV\/([\w\.]+);/i // Facebook App for iOS
  629. ], [VERSION, [NAME, 'Facebook']], [
  630. /version\/([\w\.]+).+?mobile\/\w+\s(safari)/i // Mobile Safari
  631. ], [VERSION, [NAME, 'Mobile Safari']], [
  632. /version\/([\w\.]+).+?(mobile\s?safari|safari)/i // Safari & Safari Mobile
  633. ], [VERSION, NAME], [
  634. /webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
  635. ], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [
  636. /(konqueror)\/([\w\.]+)/i, // Konqueror
  637. /(webkit|khtml)\/([\w\.]+)/i
  638. ], [NAME, VERSION], [
  639. // Gecko based
  640. /(navigator|netscape)\/([\w\.-]+)/i // Netscape
  641. ], [[NAME, 'Netscape'], VERSION], [
  642. /(swiftfox)/i, // Swiftfox
  643. /(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i,
  644. // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
  645. /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/([\w\.-]+)/i,
  646. // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
  647. /(mozilla)\/([\w\.]+).+rv\:.+gecko\/\d+/i, // Mozilla
  648. // Other
  649. /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf)[\/\s]?([\w\.]+)/i,
  650. // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf
  651. /(links)\s\(([\w\.]+)/i, // Links
  652. /(gobrowser)\/?([\w\.]+)*/i, // GoBrowser
  653. /(ice\s?browser)\/v?([\w\._]+)/i, // ICE Browser
  654. /(mosaic)[\/\s]([\w\.]+)/i // Mosaic
  655. ], [NAME, VERSION]
  656. ],
  657. engine : [[
  658. /windows.+\sedge\/([\w\.]+)/i // EdgeHTML
  659. ], [VERSION, [NAME, 'EdgeHTML']], [
  660. /(presto)\/([\w\.]+)/i, // Presto
  661. /(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m
  662. /(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i, // KHTML/Tasman/Links
  663. /(icab)[\/\s]([23]\.[\d\.]+)/i // iCab
  664. ], [NAME, VERSION], [
  665. /rv\:([\w\.]+).*(gecko)/i // Gecko
  666. ], [VERSION, NAME]
  667. ],
  668. os : [[
  669. // Windows based
  670. /microsoft\s(windows)\s(vista|xp)/i // Windows (iTunes)
  671. ], [NAME, VERSION], [
  672. /(windows)\snt\s6\.2;\s(arm)/i, // Windows RT
  673. /(windows\sphone(?:\sos)*|windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i
  674. ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [
  675. /(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i
  676. ], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [
  677. // Mobile/Embedded OS
  678. /\((bb)(10);/i // BlackBerry 10
  679. ], [[NAME, 'BlackBerry'], VERSION], [
  680. /(blackberry)\w*\/?([\w\.]+)*/i, // Blackberry
  681. /(tizen)[\/\s]([\w\.]+)/i, // Tizen
  682. /(android|webos|palm\os|qnx|bada|rim\stablet\sos|meego|contiki)[\/\s-]?([\w\.]+)*/i,
  683. // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki
  684. /linux;.+(sailfish);/i // Sailfish OS
  685. ], [NAME, VERSION], [
  686. /(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i // Symbian
  687. ], [[NAME, 'Symbian'], VERSION], [
  688. /\((series40);/i // Series 40
  689. ], [NAME], [
  690. /mozilla.+\(mobile;.+gecko.+firefox/i // Firefox OS
  691. ], [[NAME, 'Firefox OS'], VERSION], [
  692. // Console
  693. /(nintendo|playstation)\s([wids3portablevu]+)/i, // Nintendo/Playstation
  694. // GNU/Linux based
  695. /(mint)[\/\s\(]?(\w+)*/i, // Mint
  696. /(mageia|vectorlinux)[;\s]/i, // Mageia/VectorLinux
  697. /(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\/\s-]?([\w\.-]+)*/i,
  698. // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware
  699. // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus
  700. /(hurd|linux)\s?([\w\.]+)*/i, // Hurd/Linux
  701. /(gnu)\s?([\w\.]+)*/i // GNU
  702. ], [NAME, VERSION], [
  703. /(cros)\s[\w]+\s([\w\.]+\w)/i // Chromium OS
  704. ], [[NAME, 'Chromium OS'], VERSION],[
  705. // Solaris
  706. /(sunos)\s?([\w\.]+\d)*/i // Solaris
  707. ], [[NAME, 'Solaris'], VERSION], [
  708. // BSD based
  709. /\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly
  710. ], [NAME, VERSION],[
  711. /(ip[honead]+)(?:.*os\s*([\w]+)*\slike\smac|;\sopera)/i // iOS
  712. ], [[NAME, 'iOS'], [VERSION, /_/g, '.']], [
  713. /(mac\sos\sx)\s?([\w\s\.]+\w)*/i,
  714. /(macintosh|mac(?=_powerpc)\s)/i // Mac OS
  715. ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [
  716. // Other
  717. /((?:open)?solaris)[\/\s-]?([\w\.]+)*/i, // Solaris
  718. /(haiku)\s(\w+)/i, // Haiku
  719. /(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i, // AIX
  720. /(plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos|openvms)/i,
  721. // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS/OpenVMS
  722. /(unix)\s?([\w\.]+)*/i // UNIX
  723. ], [NAME, VERSION]
  724. ]
  725. };
  726. /////////////////
  727. // Constructor
  728. ////////////////
  729. var UAParser = function (uastring) {
  730. var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
  731. this.getBrowser = function () {
  732. return mapper.rgx.apply(this, regexes.browser);
  733. };
  734. this.getEngine = function () {
  735. return mapper.rgx.apply(this, regexes.engine);
  736. };
  737. this.getOS = function () {
  738. return mapper.rgx.apply(this, regexes.os);
  739. };
  740. this.getResult = function() {
  741. return {
  742. ua : this.getUA(),
  743. browser : this.getBrowser(),
  744. engine : this.getEngine(),
  745. os : this.getOS()
  746. };
  747. };
  748. this.getUA = function () {
  749. return ua;
  750. };
  751. this.setUA = function (uastring) {
  752. ua = uastring;
  753. return this;
  754. };
  755. this.setUA(ua);
  756. };
  757. return UAParser;
  758. })();
  759. function version_compare(v1, v2, operator) {
  760. // From: http://phpjs.org/functions
  761. // + original by: Philippe Jausions (http://pear.php.net/user/jausions)
  762. // + original by: Aidan Lister (http://aidanlister.com/)
  763. // + reimplemented by: Kankrelune (http://www.webfaktory.info/)
  764. // + improved by: Brett Zamir (http://brett-zamir.me)
  765. // + improved by: Scott Baker
  766. // + improved by: Theriault
  767. // * example 1: version_compare('8.2.5rc', '8.2.5a');
  768. // * returns 1: 1
  769. // * example 2: version_compare('8.2.50', '8.2.52', '<');
  770. // * returns 2: true
  771. // * example 3: version_compare('5.3.0-dev', '5.3.0');
  772. // * returns 3: -1
  773. // * example 4: version_compare('4.1.0.52','4.01.0.51');
  774. // * returns 4: 1
  775. // Important: compare must be initialized at 0.
  776. var i = 0,
  777. x = 0,
  778. compare = 0,
  779. // vm maps textual PHP versions to negatives so they're less than 0.
  780. // PHP currently defines these as CASE-SENSITIVE. It is important to
  781. // leave these as negatives so that they can come before numerical versions
  782. // and as if no letters were there to begin with.
  783. // (1alpha is < 1 and < 1.1 but > 1dev1)
  784. // If a non-numerical value can't be mapped to this table, it receives
  785. // -7 as its value.
  786. vm = {
  787. 'dev': -6,
  788. 'alpha': -5,
  789. 'a': -5,
  790. 'beta': -4,
  791. 'b': -4,
  792. 'RC': -3,
  793. 'rc': -3,
  794. '#': -2,
  795. 'p': 1,
  796. 'pl': 1
  797. },
  798. // This function will be called to prepare each version argument.
  799. // It replaces every _, -, and + with a dot.
  800. // It surrounds any nonsequence of numbers/dots with dots.
  801. // It replaces sequences of dots with a single dot.
  802. // version_compare('4..0', '4.0') == 0
  803. // Important: A string of 0 length needs to be converted into a value
  804. // even less than an unexisting value in vm (-7), hence [-8].
  805. // It's also important to not strip spaces because of this.
  806. // version_compare('', ' ') == 1
  807. prepVersion = function (v) {
  808. v = ('' + v).replace(/[_\-+]/g, '.');
  809. v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
  810. return (!v.length ? [-8] : v.split('.'));
  811. },
  812. // This converts a version component to a number.
  813. // Empty component becomes 0.
  814. // Non-numerical component becomes a negative number.
  815. // Numerical component becomes itself as an integer.
  816. numVersion = function (v) {
  817. return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
  818. };
  819. v1 = prepVersion(v1);
  820. v2 = prepVersion(v2);
  821. x = Math.max(v1.length, v2.length);
  822. for (i = 0; i < x; i++) {
  823. if (v1[i] == v2[i]) {
  824. continue;
  825. }
  826. v1[i] = numVersion(v1[i]);
  827. v2[i] = numVersion(v2[i]);
  828. if (v1[i] < v2[i]) {
  829. compare = -1;
  830. break;
  831. } else if (v1[i] > v2[i]) {
  832. compare = 1;
  833. break;
  834. }
  835. }
  836. if (!operator) {
  837. return compare;
  838. }
  839. // Important: operator is CASE-SENSITIVE.
  840. // "No operator" seems to be treated as "<."
  841. // Any other values seem to make the function return null.
  842. switch (operator) {
  843. case '>':
  844. case 'gt':
  845. return (compare > 0);
  846. case '>=':
  847. case 'ge':
  848. return (compare >= 0);
  849. case '<=':
  850. case 'le':
  851. return (compare <= 0);
  852. case '==':
  853. case '=':
  854. case 'eq':
  855. return (compare === 0);
  856. case '<>':
  857. case '!=':
  858. case 'ne':
  859. return (compare !== 0);
  860. case '':
  861. case '<':
  862. case 'lt':
  863. return (compare < 0);
  864. default:
  865. return null;
  866. }
  867. }
  868. var can = (function() {
  869. var caps = {
  870. define_property: (function() {
  871. /* // currently too much extra code required, not exactly worth it
  872. try { // as of IE8, getters/setters are supported only on DOM elements
  873. var obj = {};
  874. if (Object.defineProperty) {
  875. Object.defineProperty(obj, 'prop', {
  876. enumerable: true,
  877. configurable: true
  878. });
  879. return true;
  880. }
  881. } catch(ex) {}
  882. if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) {
  883. return true;
  884. }*/
  885. return false;
  886. }()),
  887. create_canvas: (function() {
  888. // On the S60 and BB Storm, getContext exists, but always returns undefined
  889. // so we actually have to call getContext() to verify
  890. // github.com/Modernizr/Modernizr/issues/issue/97/
  891. var el = document.createElement('canvas');
  892. return !!(el.getContext && el.getContext('2d'));
  893. }()),
  894. return_response_type: function(responseType) {
  895. try {
  896. if (Basic.inArray(responseType, ['', 'text', 'document']) !== -1) {
  897. return true;
  898. } else if (window.XMLHttpRequest) {
  899. var xhr = new XMLHttpRequest();
  900. xhr.open('get', '/'); // otherwise Gecko throws an exception
  901. if ('responseType' in xhr) {
  902. xhr.responseType = responseType;
  903. // as of 23.0.1271.64, Chrome switched from throwing exception to merely logging it to the console (why? o why?)
  904. if (xhr.responseType !== responseType) {
  905. return false;
  906. }
  907. return true;
  908. }
  909. }
  910. } catch (ex) {}
  911. return false;
  912. },
  913. // ideas for this heavily come from Modernizr (http://modernizr.com/)
  914. use_data_uri: (function() {
  915. var du = new Image();
  916. du.onload = function() {
  917. caps.use_data_uri = (du.width === 1 && du.height === 1);
  918. };
  919. setTimeout(function() {
  920. du.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
  921. }, 1);
  922. return false;
  923. }()),
  924. use_data_uri_over32kb: function() { // IE8
  925. return caps.use_data_uri && (Env.browser !== 'IE' || Env.version >= 9);
  926. },
  927. use_data_uri_of: function(bytes) {
  928. return (caps.use_data_uri && bytes < 33000 || caps.use_data_uri_over32kb());
  929. },
  930. use_fileinput: function() {
  931. if (navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/)) {
  932. return false;
  933. }
  934. var el = document.createElement('input');
  935. el.setAttribute('type', 'file');
  936. return !el.disabled;
  937. }
  938. };
  939. return function(cap) {
  940. var args = [].slice.call(arguments);
  941. args.shift(); // shift of cap
  942. return Basic.typeOf(caps[cap]) === 'function' ? caps[cap].apply(this, args) : !!caps[cap];
  943. };
  944. }());
  945. var uaResult = new UAParser().getResult();
  946. var Env = {
  947. can: can,
  948. uaParser: UAParser,
  949. browser: uaResult.browser.name,
  950. version: uaResult.browser.version,
  951. os: uaResult.os.name, // everybody intuitively types it in a lowercase for some reason
  952. osVersion: uaResult.os.version,
  953. verComp: version_compare,
  954. swf_url: "../flash/Moxie.swf",
  955. xap_url: "../silverlight/Moxie.xap",
  956. global_event_dispatcher: "moxie.core.EventTarget.instance.dispatchEvent"
  957. };
  958. // for backward compatibility
  959. // @deprecated Use `Env.os` instead
  960. Env.OS = Env.os;
  961. if (MXI_DEBUG) {
  962. Env.debug = {
  963. runtime: true,
  964. events: false
  965. };
  966. Env.log = function() {
  967. function logObj(data) {
  968. // TODO: this should recursively print out the object in a pretty way
  969. console.appendChild(document.createTextNode(data + "\n"));
  970. }
  971. var data = arguments[0];
  972. if (Basic.typeOf(data) === 'string') {
  973. data = Basic.sprintf.apply(this, arguments);
  974. }
  975. if (window && window.console && window.console.log) {
  976. window.console.log(data);
  977. } else if (document) {
  978. var console = document.getElementById('moxie-console');
  979. if (!console) {
  980. console = document.createElement('pre');
  981. console.id = 'moxie-console';
  982. //console.style.display = 'none';
  983. document.body.appendChild(console);
  984. }
  985. if (Basic.inArray(Basic.typeOf(data), ['object', 'array']) !== -1) {
  986. logObj(data);
  987. } else {
  988. console.appendChild(document.createTextNode(data + "\n"));
  989. }
  990. }
  991. };
  992. }
  993. return Env;
  994. });
  995. // Included from: src/javascript/core/I18n.js
  996. /**
  997. * I18n.js
  998. *
  999. * Copyright 2013, Moxiecode Systems AB
  1000. * Released under GPL License.
  1001. *
  1002. * License: http://www.plupload.com/license
  1003. * Contributing: http://www.plupload.com/contributing
  1004. */
  1005. define("moxie/core/I18n", [
  1006. "moxie/core/utils/Basic"
  1007. ], function(Basic) {
  1008. var i18n = {};
  1009. return {
  1010. /**
  1011. * Extends the language pack object with new items.
  1012. *
  1013. * @param {Object} pack Language pack items to add.
  1014. * @return {Object} Extended language pack object.
  1015. */
  1016. addI18n: function(pack) {
  1017. return Basic.extend(i18n, pack);
  1018. },
  1019. /**
  1020. * Translates the specified string by checking for the english string in the language pack lookup.
  1021. *
  1022. * @param {String} str String to look for.
  1023. * @return {String} Translated string or the input string if it wasn't found.
  1024. */
  1025. translate: function(str) {
  1026. return i18n[str] || str;
  1027. },
  1028. /**
  1029. * Shortcut for translate function
  1030. *
  1031. * @param {String} str String to look for.
  1032. * @return {String} Translated string or the input string if it wasn't found.
  1033. */
  1034. _: function(str) {
  1035. return this.translate(str);
  1036. },
  1037. /**
  1038. * Pseudo sprintf implementation - simple way to replace tokens with specified values.
  1039. *
  1040. * @param {String} str String with tokens
  1041. * @return {String} String with replaced tokens
  1042. */
  1043. sprintf: function(str) {
  1044. var args = [].slice.call(arguments, 1);
  1045. return str.replace(/%[a-z]/g, function() {
  1046. var value = args.shift();
  1047. return Basic.typeOf(value) !== 'undefined' ? value : '';
  1048. });
  1049. }
  1050. };
  1051. });
  1052. // Included from: src/javascript/core/utils/Mime.js
  1053. /**
  1054. * Mime.js
  1055. *
  1056. * Copyright 2013, Moxiecode Systems AB
  1057. * Released under GPL License.
  1058. *
  1059. * License: http://www.plupload.com/license
  1060. * Contributing: http://www.plupload.com/contributing
  1061. */
  1062. define("moxie/core/utils/Mime", [
  1063. "moxie/core/utils/Basic",
  1064. "moxie/core/I18n"
  1065. ], function(Basic, I18n) {
  1066. var mimeData = "" +
  1067. "application/msword,doc dot," +
  1068. "application/pdf,pdf," +
  1069. "application/pgp-signature,pgp," +
  1070. "application/postscript,ps ai eps," +
  1071. "application/rtf,rtf," +
  1072. "application/vnd.ms-excel,xls xlb," +
  1073. "application/vnd.ms-powerpoint,ppt pps pot," +
  1074. "application/zip,zip," +
  1075. "application/x-shockwave-flash,swf swfl," +
  1076. "application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx," +
  1077. "application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx," +
  1078. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx," +
  1079. "application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx," +
  1080. "application/vnd.openxmlformats-officedocument.presentationml.template,potx," +
  1081. "application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx," +
  1082. "application/x-javascript,js," +
  1083. "application/json,json," +
  1084. "audio/mpeg,mp3 mpga mpega mp2," +
  1085. "audio/x-wav,wav," +
  1086. "audio/x-m4a,m4a," +
  1087. "audio/ogg,oga ogg," +
  1088. "audio/aiff,aiff aif," +
  1089. "audio/flac,flac," +
  1090. "audio/aac,aac," +
  1091. "audio/ac3,ac3," +
  1092. "audio/x-ms-wma,wma," +
  1093. "image/bmp,bmp," +
  1094. "image/gif,gif," +
  1095. "image/jpeg,jpg jpeg jpe," +
  1096. "image/photoshop,psd," +
  1097. "image/png,png," +
  1098. "image/svg+xml,svg svgz," +
  1099. "image/tiff,tiff tif," +
  1100. "text/plain,asc txt text diff log," +
  1101. "text/html,htm html xhtml," +
  1102. "text/css,css," +
  1103. "text/csv,csv," +
  1104. "text/rtf,rtf," +
  1105. "video/mpeg,mpeg mpg mpe m2v," +
  1106. "video/quicktime,qt mov," +
  1107. "video/mp4,mp4," +
  1108. "video/x-m4v,m4v," +
  1109. "video/x-flv,flv," +
  1110. "video/x-ms-wmv,wmv," +
  1111. "video/avi,avi," +
  1112. "video/webm,webm," +
  1113. "video/3gpp,3gpp 3gp," +
  1114. "video/3gpp2,3g2," +
  1115. "video/vnd.rn-realvideo,rv," +
  1116. "video/ogg,ogv," +
  1117. "video/x-matroska,mkv," +
  1118. "application/vnd.oasis.opendocument.formula-template,otf," +
  1119. "application/octet-stream,exe";
  1120. var Mime = {
  1121. mimes: {},
  1122. extensions: {},
  1123. // Parses the default mime types string into a mimes and extensions lookup maps
  1124. addMimeType: function (mimeData) {
  1125. var items = mimeData.split(/,/), i, ii, ext;
  1126. for (i = 0; i < items.length; i += 2) {
  1127. ext = items[i + 1].split(/ /);
  1128. // extension to mime lookup
  1129. for (ii = 0; ii < ext.length; ii++) {
  1130. this.mimes[ext[ii]] = items[i];
  1131. }
  1132. // mime to extension lookup
  1133. this.extensions[items[i]] = ext;
  1134. }
  1135. },
  1136. extList2mimes: function (filters, addMissingExtensions) {
  1137. var self = this, ext, i, ii, type, mimes = [];
  1138. // convert extensions to mime types list
  1139. for (i = 0; i < filters.length; i++) {
  1140. ext = filters[i].extensions.split(/\s*,\s*/);
  1141. for (ii = 0; ii < ext.length; ii++) {
  1142. // if there's an asterisk in the list, then accept attribute is not required
  1143. if (ext[ii] === '*') {
  1144. return [];
  1145. }
  1146. type = self.mimes[ext[ii]];
  1147. if (type && Basic.inArray(type, mimes) === -1) {
  1148. mimes.push(type);
  1149. }
  1150. // future browsers should filter by extension, finally
  1151. if (addMissingExtensions && /^\w+$/.test(ext[ii])) {
  1152. mimes.push('.' + ext[ii]);
  1153. } else if (!type) {
  1154. // if we have no type in our map, then accept all
  1155. return [];
  1156. }
  1157. }
  1158. }
  1159. return mimes;
  1160. },
  1161. mimes2exts: function(mimes) {
  1162. var self = this, exts = [];
  1163. Basic.each(mimes, function(mime) {
  1164. if (mime === '*') {
  1165. exts = [];
  1166. return false;
  1167. }
  1168. // check if this thing looks like mime type
  1169. var m = mime.match(/^(\w+)\/(\*|\w+)$/);
  1170. if (m) {
  1171. if (m[2] === '*') {
  1172. // wildcard mime type detected
  1173. Basic.each(self.extensions, function(arr, mime) {
  1174. if ((new RegExp('^' + m[1] + '/')).test(mime)) {
  1175. [].push.apply(exts, self.extensions[mime]);
  1176. }
  1177. });
  1178. } else if (self.extensions[mime]) {
  1179. [].push.apply(exts, self.extensions[mime]);
  1180. }
  1181. }
  1182. });
  1183. return exts;
  1184. },
  1185. mimes2extList: function(mimes) {
  1186. var accept = [], exts = [];
  1187. if (Basic.typeOf(mimes) === 'string') {
  1188. mimes = Basic.trim(mimes).split(/\s*,\s*/);
  1189. }
  1190. exts = this.mimes2exts(mimes);
  1191. accept.push({
  1192. title: I18n.translate('Files'),
  1193. extensions: exts.length ? exts.join(',') : '*'
  1194. });
  1195. // save original mimes string
  1196. accept.mimes = mimes;
  1197. return accept;
  1198. },
  1199. getFileExtension: function(fileName) {
  1200. var matches = fileName && fileName.match(/\.([^.]+)$/);
  1201. if (matches) {
  1202. return matches[1].toLowerCase();
  1203. }
  1204. return '';
  1205. },
  1206. getFileMime: function(fileName) {
  1207. return this.mimes[this.getFileExtension(fileName)] || '';
  1208. }
  1209. };
  1210. Mime.addMimeType(mimeData);
  1211. return Mime;
  1212. });
  1213. // Included from: src/javascript/core/utils/Dom.js
  1214. /**
  1215. * Dom.js
  1216. *
  1217. * Copyright 2013, Moxiecode Systems AB
  1218. * Released under GPL License.
  1219. *
  1220. * License: http://www.plupload.com/license
  1221. * Contributing: http://www.plupload.com/contributing
  1222. */
  1223. define('moxie/core/utils/Dom', ['moxie/core/utils/Env'], function(Env) {
  1224. /**
  1225. Get DOM Element by it's id.
  1226. @method get
  1227. @for Utils
  1228. @param {String} id Identifier of the DOM Element
  1229. @return {DOMElement}
  1230. */
  1231. var get = function(id) {
  1232. if (typeof id !== 'string') {
  1233. return id;
  1234. }
  1235. return document.getElementById(id);
  1236. };
  1237. /**
  1238. Checks if specified DOM element has specified class.
  1239. @method hasClass
  1240. @static
  1241. @param {Object} obj DOM element like object to add handler to.
  1242. @param {String} name Class name
  1243. */
  1244. var hasClass = function(obj, name) {
  1245. if (!obj.className) {
  1246. return false;
  1247. }
  1248. var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)");
  1249. return regExp.test(obj.className);
  1250. };
  1251. /**
  1252. Adds specified className to specified DOM element.
  1253. @method addClass
  1254. @static
  1255. @param {Object} obj DOM element like object to add handler to.
  1256. @param {String} name Class name
  1257. */
  1258. var addClass = function(obj, name) {
  1259. if (!hasClass(obj, name)) {
  1260. obj.className = !obj.className ? name : obj.className.replace(/\s+$/, '') + ' ' + name;
  1261. }
  1262. };
  1263. /**
  1264. Removes specified className from specified DOM element.
  1265. @method removeClass
  1266. @static
  1267. @param {Object} obj DOM element like object to add handler to.
  1268. @param {String} name Class name
  1269. */
  1270. var removeClass = function(obj, name) {
  1271. if (obj.className) {
  1272. var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)");
  1273. obj.className = obj.className.replace(regExp, function($0, $1, $2) {
  1274. return $1 === ' ' && $2 === ' ' ? ' ' : '';
  1275. });
  1276. }
  1277. };
  1278. /**
  1279. Returns a given computed style of a DOM element.
  1280. @method getStyle
  1281. @static
  1282. @param {Object} obj DOM element like object.
  1283. @param {String} name Style you want to get from the DOM element
  1284. */
  1285. var getStyle = function(obj, name) {
  1286. if (obj.currentStyle) {
  1287. return obj.currentStyle[name];
  1288. } else if (window.getComputedStyle) {
  1289. return window.getComputedStyle(obj, null)[name];
  1290. }
  1291. };
  1292. /**
  1293. Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
  1294. @method getPos
  1295. @static
  1296. @param {Element} node HTML element or element id to get x, y position from.
  1297. @param {Element} root Optional root element to stop calculations at.
  1298. @return {object} Absolute position of the specified element object with x, y fields.
  1299. */
  1300. var getPos = function(node, root) {
  1301. var x = 0, y = 0, parent, doc = document, nodeRect, rootRect;
  1302. node = node;
  1303. root = root || doc.body;
  1304. // Returns the x, y cordinate for an element on IE 6 and IE 7
  1305. function getIEPos(node) {
  1306. var bodyElm, rect, x = 0, y = 0;
  1307. if (node) {
  1308. rect = node.getBoundingClientRect();
  1309. bodyElm = doc.compatMode === "CSS1Compat" ? doc.documentElement : doc.body;
  1310. x = rect.left + bodyElm.scrollLeft;
  1311. y = rect.top + bodyElm.scrollTop;
  1312. }
  1313. return {
  1314. x : x,
  1315. y : y
  1316. };
  1317. }
  1318. // Use getBoundingClientRect on IE 6 and IE 7 but not on IE 8 in standards mode
  1319. if (node && node.getBoundingClientRect && Env.browser === 'IE' && (!doc.documentMode || doc.documentMode < 8)) {
  1320. nodeRect = getIEPos(node);
  1321. rootRect = getIEPos(root);
  1322. return {
  1323. x : nodeRect.x - rootRect.x,
  1324. y : nodeRect.y - rootRect.y
  1325. };
  1326. }
  1327. parent = node;
  1328. while (parent && parent != root && parent.nodeType) {
  1329. x += parent.offsetLeft || 0;
  1330. y += parent.offsetTop || 0;
  1331. parent = parent.offsetParent;
  1332. }
  1333. parent = node.parentNode;
  1334. while (parent && parent != root && parent.nodeType) {
  1335. x -= parent.scrollLeft || 0;
  1336. y -= parent.scrollTop || 0;
  1337. parent = parent.parentNode;
  1338. }
  1339. return {
  1340. x : x,
  1341. y : y
  1342. };
  1343. };
  1344. /**
  1345. Returns the size of the specified node in pixels.
  1346. @method getSize
  1347. @static
  1348. @param {Node} node Node to get the size of.
  1349. @return {Object} Object with a w and h property.
  1350. */
  1351. var getSize = function(node) {
  1352. return {
  1353. w : node.offsetWidth || node.clientWidth,
  1354. h : node.offsetHeight || node.clientHeight
  1355. };
  1356. };
  1357. return {
  1358. get: get,
  1359. hasClass: hasClass,
  1360. addClass: addClass,
  1361. removeClass: removeClass,
  1362. getStyle: getStyle,
  1363. getPos: getPos,
  1364. getSize: getSize
  1365. };
  1366. });
  1367. // Included from: src/javascript/core/Exceptions.js
  1368. /**
  1369. * Exceptions.js
  1370. *
  1371. * Copyright 2013, Moxiecode Systems AB
  1372. * Released under GPL License.
  1373. *
  1374. * License: http://www.plupload.com/license
  1375. * Contributing: http://www.plupload.com/contributing
  1376. */
  1377. define('moxie/core/Exceptions', [
  1378. 'moxie/core/utils/Basic'
  1379. ], function(Basic) {
  1380. function _findKey(obj, value) {
  1381. var key;
  1382. for (key in obj) {
  1383. if (obj[key] === value) {
  1384. return key;
  1385. }
  1386. }
  1387. return null;
  1388. }
  1389. return {
  1390. RuntimeError: (function() {
  1391. var namecodes = {
  1392. NOT_INIT_ERR: 1,
  1393. NOT_SUPPORTED_ERR: 9,
  1394. JS_ERR: 4
  1395. };
  1396. function RuntimeError(code) {
  1397. this.code = code;
  1398. this.name = _findKey(namecodes, code);
  1399. this.message = this.name + ": RuntimeError " + this.code;
  1400. }
  1401. Basic.extend(RuntimeError, namecodes);
  1402. RuntimeError.prototype = Error.prototype;
  1403. return RuntimeError;
  1404. }()),
  1405. OperationNotAllowedException: (function() {
  1406. function OperationNotAllowedException(code) {
  1407. this.code = code;
  1408. this.name = 'OperationNotAllowedException';
  1409. }
  1410. Basic.extend(OperationNotAllowedException, {
  1411. NOT_ALLOWED_ERR: 1
  1412. });
  1413. OperationNotAllowedException.prototype = Error.prototype;
  1414. return OperationNotAllowedException;
  1415. }()),
  1416. ImageError: (function() {
  1417. var namecodes = {
  1418. WRONG_FORMAT: 1,
  1419. MAX_RESOLUTION_ERR: 2,
  1420. INVALID_META_ERR: 3
  1421. };
  1422. function ImageError(code) {
  1423. this.code = code;
  1424. this.name = _findKey(namecodes, code);
  1425. this.message = this.name + ": ImageError " + this.code;
  1426. }
  1427. Basic.extend(ImageError, namecodes);
  1428. ImageError.prototype = Error.prototype;
  1429. return ImageError;
  1430. }()),
  1431. FileException: (function() {
  1432. var namecodes = {
  1433. NOT_FOUND_ERR: 1,
  1434. SECURITY_ERR: 2,
  1435. ABORT_ERR: 3,
  1436. NOT_READABLE_ERR: 4,
  1437. ENCODING_ERR: 5,
  1438. NO_MODIFICATION_ALLOWED_ERR: 6,
  1439. INVALID_STATE_ERR: 7,
  1440. SYNTAX_ERR: 8
  1441. };
  1442. function FileException(code) {
  1443. this.code = code;
  1444. this.name = _findKey(namecodes, code);
  1445. this.message = this.name + ": FileException " + this.code;
  1446. }
  1447. Basic.extend(FileException, namecodes);
  1448. FileException.prototype = Error.prototype;
  1449. return FileException;
  1450. }()),
  1451. DOMException: (function() {
  1452. var namecodes = {
  1453. INDEX_SIZE_ERR: 1,
  1454. DOMSTRING_SIZE_ERR: 2,
  1455. HIERARCHY_REQUEST_ERR: 3,
  1456. WRONG_DOCUMENT_ERR: 4,
  1457. INVALID_CHARACTER_ERR: 5,
  1458. NO_DATA_ALLOWED_ERR: 6,
  1459. NO_MODIFICATION_ALLOWED_ERR: 7,
  1460. NOT_FOUND_ERR: 8,
  1461. NOT_SUPPORTED_ERR: 9,
  1462. INUSE_ATTRIBUTE_ERR: 10,
  1463. INVALID_STATE_ERR: 11,
  1464. SYNTAX_ERR: 12,
  1465. INVALID_MODIFICATION_ERR: 13,
  1466. NAMESPACE_ERR: 14,
  1467. INVALID_ACCESS_ERR: 15,
  1468. VALIDATION_ERR: 16,
  1469. TYPE_MISMATCH_ERR: 17,
  1470. SECURITY_ERR: 18,
  1471. NETWORK_ERR: 19,
  1472. ABORT_ERR: 20,
  1473. URL_MISMATCH_ERR: 21,
  1474. QUOTA_EXCEEDED_ERR: 22,
  1475. TIMEOUT_ERR: 23,
  1476. INVALID_NODE_TYPE_ERR: 24,
  1477. DATA_CLONE_ERR: 25
  1478. };
  1479. function DOMException(code) {
  1480. this.code = code;
  1481. this.name = _findKey(namecodes, code);
  1482. this.message = this.name + ": DOMException " + this.code;
  1483. }
  1484. Basic.extend(DOMException, namecodes);
  1485. DOMException.prototype = Error.prototype;
  1486. return DOMException;
  1487. }()),
  1488. EventException: (function() {
  1489. function EventException(code) {
  1490. this.code = code;
  1491. this.name = 'EventException';
  1492. }
  1493. Basic.extend(EventException, {
  1494. UNSPECIFIED_EVENT_TYPE_ERR: 0
  1495. });
  1496. EventException.prototype = Error.prototype;
  1497. return EventException;
  1498. }())
  1499. };
  1500. });
  1501. // Included from: src/javascript/core/EventTarget.js
  1502. /**
  1503. * EventTarget.js
  1504. *
  1505. * Copyright 2013, Moxiecode Systems AB
  1506. * Released under GPL License.
  1507. *
  1508. * License: http://www.plupload.com/license
  1509. * Contributing: http://www.plupload.com/contributing
  1510. */
  1511. define('moxie/core/EventTarget', [
  1512. 'moxie/core/utils/Env',
  1513. 'moxie/core/Exceptions',
  1514. 'moxie/core/utils/Basic'
  1515. ], function(Env, x, Basic) {
  1516. /**
  1517. Parent object for all event dispatching components and objects
  1518. @class EventTarget
  1519. @constructor EventTarget
  1520. */
  1521. function EventTarget() {
  1522. // hash of event listeners by object uid
  1523. var eventpool = {};
  1524. Basic.extend(this, {
  1525. /**
  1526. Unique id of the event dispatcher, usually overriden by children
  1527. @property uid
  1528. @type String
  1529. */
  1530. uid: null,
  1531. /**
  1532. Can be called from within a child in order to acquire uniqie id in automated manner
  1533. @method init
  1534. */
  1535. init: function() {
  1536. if (!this.uid) {
  1537. this.uid = Basic.guid('uid_');
  1538. }
  1539. },
  1540. /**
  1541. Register a handler to a specific event dispatched by the object
  1542. @method addEventListener
  1543. @param {String} type Type or basically a name of the event to subscribe to
  1544. @param {Function} fn Callback function that will be called when event happens
  1545. @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
  1546. @param {Object} [scope=this] A scope to invoke event handler in
  1547. */
  1548. addEventListener: function(type, fn, priority, scope) {
  1549. var self = this, list;
  1550. // without uid no event handlers can be added, so make sure we got one
  1551. if (!this.hasOwnProperty('uid')) {
  1552. this.uid = Basic.guid('uid_');
  1553. }
  1554. type = Basic.trim(type);
  1555. if (/\s/.test(type)) {
  1556. // multiple event types were passed for one handler
  1557. Basic.each(type.split(/\s+/), function(type) {
  1558. self.addEventListener(type, fn, priority, scope);
  1559. });
  1560. return;
  1561. }
  1562. type = type.toLowerCase();
  1563. priority = parseInt(priority, 10) || 0;
  1564. list = eventpool[this.uid] && eventpool[this.uid][type] || [];
  1565. list.push({fn : fn, priority : priority, scope : scope || this});
  1566. if (!eventpool[this.uid]) {
  1567. eventpool[this.uid] = {};
  1568. }
  1569. eventpool[this.uid][type] = list;
  1570. },
  1571. /**
  1572. Check if any handlers were registered to the specified event
  1573. @method hasEventListener
  1574. @param {String} type Type or basically a name of the event to check
  1575. @return {Mixed} Returns a handler if it was found and false, if - not
  1576. */
  1577. hasEventListener: function(type) {
  1578. var list = type ? eventpool[this.uid] && eventpool[this.uid][type] : eventpool[this.uid];
  1579. return list ? list : false;
  1580. },
  1581. /**
  1582. Unregister the handler from the event, or if former was not specified - unregister all handlers
  1583. @method removeEventListener
  1584. @param {String} type Type or basically a name of the event
  1585. @param {Function} [fn] Handler to unregister
  1586. */
  1587. removeEventListener: function(type, fn) {
  1588. type = type.toLowerCase();
  1589. var list = eventpool[this.uid] && eventpool[this.uid][type], i;
  1590. if (list) {
  1591. if (fn) {
  1592. for (i = list.length - 1; i >= 0; i--) {
  1593. if (list[i].fn === fn) {
  1594. list.splice(i, 1);
  1595. break;
  1596. }
  1597. }
  1598. } else {
  1599. list = [];
  1600. }
  1601. // delete event list if it has become empty
  1602. if (!list.length) {
  1603. delete eventpool[this.uid][type];
  1604. // and object specific entry in a hash if it has no more listeners attached
  1605. if (Basic.isEmptyObj(eventpool[this.uid])) {
  1606. delete eventpool[this.uid];
  1607. }
  1608. }
  1609. }
  1610. },
  1611. /**
  1612. Remove all event handlers from the object
  1613. @method removeAllEventListeners
  1614. */
  1615. removeAllEventListeners: function() {
  1616. if (eventpool[this.uid]) {
  1617. delete eventpool[this.uid];
  1618. }
  1619. },
  1620. /**
  1621. Dispatch the event
  1622. @method dispatchEvent
  1623. @param {String/Object} Type of event or event object to dispatch
  1624. @param {Mixed} [...] Variable number of arguments to be passed to a handlers
  1625. @return {Boolean} true by default and false if any handler returned false
  1626. */
  1627. dispatchEvent: function(type) {
  1628. var uid, list, args, tmpEvt, evt = {}, result = true, undef;
  1629. if (Basic.typeOf(type) !== 'string') {
  1630. // we can't use original object directly (because of Silverlight)
  1631. tmpEvt = type;
  1632. if (Basic.typeOf(tmpEvt.type) === 'string') {
  1633. type = tmpEvt.type;
  1634. if (tmpEvt.total !== undef && tmpEvt.loaded !== undef) { // progress event
  1635. evt.total = tmpEvt.total;
  1636. evt.loaded = tmpEvt.loaded;
  1637. }
  1638. evt.async = tmpEvt.async || false;
  1639. } else {
  1640. throw new x.EventException(x.EventException.UNSPECIFIED_EVENT_TYPE_ERR);
  1641. }
  1642. }
  1643. // check if event is meant to be dispatched on an object having specific uid
  1644. if (type.indexOf('::') !== -1) {
  1645. (function(arr) {
  1646. uid = arr[0];
  1647. type = arr[1];
  1648. }(type.split('::')));
  1649. } else {
  1650. uid = this.uid;
  1651. }
  1652. type = type.toLowerCase();
  1653. list = eventpool[uid] && eventpool[uid][type];
  1654. if (list) {
  1655. // sort event list by prority
  1656. list.sort(function(a, b) { return b.priority - a.priority; });
  1657. args = [].slice.call(arguments);
  1658. // first argument will be pseudo-event object
  1659. args.shift();
  1660. evt.type = type;
  1661. args.unshift(evt);
  1662. if (MXI_DEBUG && Env.debug.events) {
  1663. Env.log("Event '%s' fired on %u", evt.type, uid);
  1664. }
  1665. // Dispatch event to all listeners
  1666. var queue = [];
  1667. Basic.each(list, function(handler) {
  1668. // explicitly set the target, otherwise events fired from shims do not get it
  1669. args[0].target = handler.scope;
  1670. // if event is marked as async, detach the handler
  1671. if (evt.async) {
  1672. queue.push(function(cb) {
  1673. setTimeout(function() {
  1674. cb(handler.fn.apply(handler.scope, args) === false);
  1675. }, 1);
  1676. });
  1677. } else {
  1678. queue.push(function(cb) {
  1679. cb(handler.fn.apply(handler.scope, args) === false); // if handler returns false stop propagation
  1680. });
  1681. }
  1682. });
  1683. if (queue.length) {
  1684. Basic.inSeries(queue, function(err) {
  1685. result = !err;
  1686. });
  1687. }
  1688. }
  1689. return result;
  1690. },
  1691. /**
  1692. Alias for addEventListener
  1693. @method bind
  1694. @protected
  1695. */
  1696. bind: function() {
  1697. this.addEventListener.apply(this, arguments);
  1698. },
  1699. /**
  1700. Alias for removeEventListener
  1701. @method unbind
  1702. @protected
  1703. */
  1704. unbind: function() {
  1705. this.removeEventListener.apply(this, arguments);
  1706. },
  1707. /**
  1708. Alias for removeAllEventListeners
  1709. @method unbindAll
  1710. @protected
  1711. */
  1712. unbindAll: function() {
  1713. this.removeAllEventListeners.apply(this, arguments);
  1714. },
  1715. /**
  1716. Alias for dispatchEvent
  1717. @method trigger
  1718. @protected
  1719. */
  1720. trigger: function() {
  1721. return this.dispatchEvent.apply(this, arguments);
  1722. },
  1723. /**
  1724. Handle properties of on[event] type.
  1725. @method handleEventProps
  1726. @private
  1727. */
  1728. handleEventProps: function(dispatches) {
  1729. var self = this;
  1730. this.bind(dispatches.join(' '), function(e) {
  1731. var prop = 'on' + e.type.toLowerCase();
  1732. if (Basic.typeOf(this[prop]) === 'function') {
  1733. this[prop].apply(this, arguments);
  1734. }
  1735. });
  1736. // object must have defined event properties, even if it doesn't make use of them
  1737. Basic.each(dispatches, function(prop) {
  1738. prop = 'on' + prop.toLowerCase(prop);
  1739. if (Basic.typeOf(self[prop]) === 'undefined') {
  1740. self[prop] = null;
  1741. }
  1742. });
  1743. }
  1744. });
  1745. }
  1746. EventTarget.instance = new EventTarget();
  1747. return EventTarget;
  1748. });
  1749. // Included from: src/javascript/runtime/Runtime.js
  1750. /**
  1751. * Runtime.js
  1752. *
  1753. * Copyright 2013, Moxiecode Systems AB
  1754. * Released under GPL License.
  1755. *
  1756. * License: http://www.plupload.com/license
  1757. * Contributing: http://www.plupload.com/contributing
  1758. */
  1759. define('moxie/runtime/Runtime', [
  1760. "moxie/core/utils/Env",
  1761. "moxie/core/utils/Basic",
  1762. "moxie/core/utils/Dom",
  1763. "moxie/core/EventTarget"
  1764. ], function(Env, Basic, Dom, EventTarget) {
  1765. var runtimeConstructors = {}, runtimes = {};
  1766. /**
  1767. Common set of methods and properties for every runtime instance
  1768. @class Runtime
  1769. @param {Object} options
  1770. @param {String} type Sanitized name of the runtime
  1771. @param {Object} [caps] Set of capabilities that differentiate specified runtime
  1772. @param {Object} [modeCaps] Set of capabilities that do require specific operational mode
  1773. @param {String} [preferredMode='browser'] Preferred operational mode to choose if no required capabilities were requested
  1774. */
  1775. function Runtime(options, type, caps, modeCaps, preferredMode) {
  1776. /**
  1777. Dispatched when runtime is initialized and ready.
  1778. Results in RuntimeInit on a connected component.
  1779. @event Init
  1780. */
  1781. /**
  1782. Dispatched when runtime fails to initialize.
  1783. Results in RuntimeError on a connected component.
  1784. @event Error
  1785. */
  1786. var self = this
  1787. , _shim
  1788. , _uid = Basic.guid(type + '_')
  1789. , defaultMode = preferredMode || 'browser'
  1790. ;
  1791. options = options || {};
  1792. // register runtime in private hash
  1793. runtimes[_uid] = this;
  1794. /**
  1795. Default set of capabilities, which can be redifined later by specific runtime
  1796. @private
  1797. @property caps
  1798. @type Object
  1799. */
  1800. caps = Basic.extend({
  1801. // Runtime can:
  1802. // provide access to raw binary data of the file
  1803. access_binary: false,
  1804. // provide access to raw binary data of the image (image extension is optional)
  1805. access_image_binary: false,
  1806. // display binary data as thumbs for example
  1807. display_media: false,
  1808. // make cross-domain requests
  1809. do_cors: false,
  1810. // accept files dragged and dropped from the desktop
  1811. drag_and_drop: false,
  1812. // filter files in selection dialog by their extensions
  1813. filter_by_extension: true,
  1814. // resize image (and manipulate it raw data of any file in general)
  1815. resize_image: false,
  1816. // periodically report how many bytes of total in the file were uploaded (loaded)
  1817. report_upload_progress: false,
  1818. // provide access to the headers of http response
  1819. return_response_headers: false,
  1820. // support response of specific type, which should be passed as an argument
  1821. // e.g. runtime.can('return_response_type', 'blob')
  1822. return_response_type: false,
  1823. // return http status code of the response
  1824. return_status_code: true,
  1825. // send custom http header with the request
  1826. send_custom_headers: false,
  1827. // pick up the files from a dialog
  1828. select_file: false,
  1829. // select whole folder in file browse dialog
  1830. select_folder: false,
  1831. // select multiple files at once in file browse dialog
  1832. select_multiple: true,
  1833. // send raw binary data, that is generated after image resizing or manipulation of other kind
  1834. send_binary_string: false,
  1835. // send cookies with http request and therefore retain session
  1836. send_browser_cookies: true,
  1837. // send data formatted as multipart/form-data
  1838. send_multipart: true,
  1839. // slice the file or blob to smaller parts
  1840. slice_blob: false,
  1841. // upload file without preloading it to memory, stream it out directly from disk
  1842. stream_upload: false,
  1843. // programmatically trigger file browse dialog
  1844. summon_file_dialog: false,
  1845. // upload file of specific size, size should be passed as argument
  1846. // e.g. runtime.can('upload_filesize', '500mb')
  1847. upload_filesize: true,
  1848. // initiate http request with specific http method, method should be passed as argument
  1849. // e.g. runtime.can('use_http_method', 'put')
  1850. use_http_method: true
  1851. }, caps);
  1852. // default to the mode that is compatible with preferred caps
  1853. if (options.preferred_caps) {
  1854. defaultMode = Runtime.getMode(modeCaps, options.preferred_caps, defaultMode);
  1855. }
  1856. if (MXI_DEBUG && Env.debug.runtime) {
  1857. Env.log("\tdefault mode: %s", defaultMode);
  1858. }
  1859. // small extension factory here (is meant to be extended with actual extensions constructors)
  1860. _shim = (function() {
  1861. var objpool = {};
  1862. return {
  1863. exec: function(uid, comp, fn, args) {
  1864. if (_shim[comp]) {
  1865. if (!objpool[uid]) {
  1866. objpool[uid] = {
  1867. context: this,
  1868. instance: new _shim[comp]()
  1869. };
  1870. }
  1871. if (objpool[uid].instance[fn]) {
  1872. return objpool[uid].instance[fn].apply(this, args);
  1873. }
  1874. }
  1875. },
  1876. removeInstance: function(uid) {
  1877. delete objpool[uid];
  1878. },
  1879. removeAllInstances: function() {
  1880. var self = this;
  1881. Basic.each(objpool, function(obj, uid) {
  1882. if (Basic.typeOf(obj.instance.destroy) === 'function') {
  1883. obj.instance.destroy.call(obj.context);
  1884. }
  1885. self.removeInstance(uid);
  1886. });
  1887. }
  1888. };
  1889. }());
  1890. // public methods
  1891. Basic.extend(this, {
  1892. /**
  1893. Specifies whether runtime instance was initialized or not
  1894. @property initialized
  1895. @type {Boolean}
  1896. @default false
  1897. */
  1898. initialized: false, // shims require this flag to stop initialization retries
  1899. /**
  1900. Unique ID of the runtime
  1901. @property uid
  1902. @type {String}
  1903. */
  1904. uid: _uid,
  1905. /**
  1906. Runtime type (e.g. flash, html5, etc)
  1907. @property type
  1908. @type {String}
  1909. */
  1910. type: type,
  1911. /**
  1912. Runtime (not native one) may operate in browser or client mode.
  1913. @property mode
  1914. @private
  1915. @type {String|Boolean} current mode or false, if none possible
  1916. */
  1917. mode: Runtime.getMode(modeCaps, (options.required_caps), defaultMode),
  1918. /**
  1919. id of the DOM container for the runtime (if available)
  1920. @property shimid
  1921. @type {String}
  1922. */
  1923. shimid: _uid + '_container',
  1924. /**
  1925. Number of connected clients. If equal to zero, runtime can be destroyed
  1926. @property clients
  1927. @type {Number}
  1928. */
  1929. clients: 0,
  1930. /**
  1931. Runtime initialization options
  1932. @property options
  1933. @type {Object}
  1934. */
  1935. options: options,
  1936. /**
  1937. Checks if the runtime has specific capability
  1938. @method can
  1939. @param {String} cap Name of capability to check
  1940. @param {Mixed} [value] If passed, capability should somehow correlate to the value
  1941. @param {Object} [refCaps] Set of capabilities to check the specified cap against (defaults to internal set)
  1942. @return {Boolean} true if runtime has such capability and false, if - not
  1943. */
  1944. can: function(cap, value) {
  1945. var refCaps = arguments[2] || caps;
  1946. // if cap var is a comma-separated list of caps, convert it to object (key/value)
  1947. if (Basic.typeOf(cap) === 'string' && Basic.typeOf(value) === 'undefined') {
  1948. cap = Runtime.parseCaps(cap);
  1949. }
  1950. if (Basic.typeOf(cap) === 'object') {
  1951. for (var key in cap) {
  1952. if (!this.can(key, cap[key], refCaps)) {
  1953. return false;
  1954. }
  1955. }
  1956. return true;
  1957. }
  1958. // check the individual cap
  1959. if (Basic.typeOf(refCaps[cap]) === 'function') {
  1960. return refCaps[cap].call(this, value);
  1961. } else {
  1962. return (value === refCaps[cap]);
  1963. }
  1964. },
  1965. /**
  1966. Returns container for the runtime as DOM element
  1967. @method getShimContainer
  1968. @return {DOMElement}
  1969. */
  1970. getShimContainer: function() {
  1971. var container, shimContainer = Dom.get(this.shimid);
  1972. // if no container for shim, create one
  1973. if (!shimContainer) {
  1974. container = this.options.container ? Dom.get(this.options.container) : document.body;
  1975. // create shim container and insert it at an absolute position into the outer container
  1976. shimContainer = document.createElement('div');
  1977. shimContainer.id = this.shimid;
  1978. shimContainer.className = 'moxie-shim moxie-shim-' + this.type;
  1979. Basic.extend(shimContainer.style, {
  1980. position: 'absolute',
  1981. top: '0px',
  1982. left: '0px',
  1983. width: '1px',
  1984. height: '1px',
  1985. overflow: 'hidden'
  1986. });
  1987. container.appendChild(shimContainer);
  1988. container = null;
  1989. }
  1990. return shimContainer;
  1991. },
  1992. /**
  1993. Returns runtime as DOM element (if appropriate)
  1994. @method getShim
  1995. @return {DOMElement}
  1996. */
  1997. getShim: function() {
  1998. return _shim;
  1999. },
  2000. /**
  2001. Invokes a method within the runtime itself (might differ across the runtimes)
  2002. @method shimExec
  2003. @param {Mixed} []
  2004. @protected
  2005. @return {Mixed} Depends on the action and component
  2006. */
  2007. shimExec: function(component, action) {
  2008. var args = [].slice.call(arguments, 2);
  2009. return self.getShim().exec.call(this, this.uid, component, action, args);
  2010. },
  2011. /**
  2012. Operaional interface that is used by components to invoke specific actions on the runtime
  2013. (is invoked in the scope of component)
  2014. @method exec
  2015. @param {Mixed} []*
  2016. @protected
  2017. @return {Mixed} Depends on the action and component
  2018. */
  2019. exec: function(component, action) { // this is called in the context of component, not runtime
  2020. var args = [].slice.call(arguments, 2);
  2021. if (self[component] && self[component][action]) {
  2022. return self[component][action].apply(this, args);
  2023. }
  2024. return self.shimExec.apply(this, arguments);
  2025. },
  2026. /**
  2027. Destroys the runtime (removes all events and deletes DOM structures)
  2028. @method destroy
  2029. */
  2030. destroy: function() {
  2031. if (!self) {
  2032. return; // obviously already destroyed
  2033. }
  2034. var shimContainer = Dom.get(this.shimid);
  2035. if (shimContainer) {
  2036. shimContainer.parentNode.removeChild(shimContainer);
  2037. }
  2038. if (_shim) {
  2039. _shim.removeAllInstances();
  2040. }
  2041. this.unbindAll();
  2042. delete runtimes[this.uid];
  2043. this.uid = null; // mark this runtime as destroyed
  2044. _uid = self = _shim = shimContainer = null;
  2045. }
  2046. });
  2047. // once we got the mode, test against all caps
  2048. if (this.mode && options.required_caps && !this.can(options.required_caps)) {
  2049. this.mode = false;
  2050. }
  2051. }
  2052. /**
  2053. Default order to try different runtime types
  2054. @property order
  2055. @type String
  2056. @static
  2057. */
  2058. Runtime.order = 'html5,flash,silverlight,html4';
  2059. /**
  2060. Retrieves runtime from private hash by it's uid
  2061. @method getRuntime
  2062. @private
  2063. @static
  2064. @param {String} uid Unique identifier of the runtime
  2065. @return {Runtime|Boolean} Returns runtime, if it exists and false, if - not
  2066. */
  2067. Runtime.getRuntime = function(uid) {
  2068. return runtimes[uid] ? runtimes[uid] : false;
  2069. };
  2070. /**
  2071. Register constructor for the Runtime of new (or perhaps modified) type
  2072. @method addConstructor
  2073. @static
  2074. @param {String} type Runtime type (e.g. flash, html5, etc)
  2075. @param {Function} construct Constructor for the Runtime type
  2076. */
  2077. Runtime.addConstructor = function(type, constructor) {
  2078. constructor.prototype = EventTarget.instance;
  2079. runtimeConstructors[type] = constructor;
  2080. };
  2081. /**
  2082. Get the constructor for the specified type.
  2083. method getConstructor
  2084. @static
  2085. @param {String} type Runtime type (e.g. flash, html5, etc)
  2086. @return {Function} Constructor for the Runtime type
  2087. */
  2088. Runtime.getConstructor = function(type) {
  2089. return runtimeConstructors[type] || null;
  2090. };
  2091. /**
  2092. Get info about the runtime (uid, type, capabilities)
  2093. @method getInfo
  2094. @static
  2095. @param {String} uid Unique identifier of the runtime
  2096. @return {Mixed} Info object or null if runtime doesn't exist
  2097. */
  2098. Runtime.getInfo = function(uid) {
  2099. var runtime = Runtime.getRuntime(uid);
  2100. if (runtime) {
  2101. return {
  2102. uid: runtime.uid,
  2103. type: runtime.type,
  2104. mode: runtime.mode,
  2105. can: function() {
  2106. return runtime.can.apply(runtime, arguments);
  2107. }
  2108. };
  2109. }
  2110. return null;
  2111. };
  2112. /**
  2113. Convert caps represented by a comma-separated string to the object representation.
  2114. @method parseCaps
  2115. @static
  2116. @param {String} capStr Comma-separated list of capabilities
  2117. @return {Object}
  2118. */
  2119. Runtime.parseCaps = function(capStr) {
  2120. var capObj = {};
  2121. if (Basic.typeOf(capStr) !== 'string') {
  2122. return capStr || {};
  2123. }
  2124. Basic.each(capStr.split(','), function(key) {
  2125. capObj[key] = true; // we assume it to be - true
  2126. });
  2127. return capObj;
  2128. };
  2129. /**
  2130. Test the specified runtime for specific capabilities.
  2131. @method can
  2132. @static
  2133. @param {String} type Runtime type (e.g. flash, html5, etc)
  2134. @param {String|Object} caps Set of capabilities to check
  2135. @return {Boolean} Result of the test
  2136. */
  2137. Runtime.can = function(type, caps) {
  2138. var runtime
  2139. , constructor = Runtime.getConstructor(type)
  2140. , mode
  2141. ;
  2142. if (constructor) {
  2143. runtime = new constructor({
  2144. required_caps: caps
  2145. });
  2146. mode = runtime.mode;
  2147. runtime.destroy();
  2148. return !!mode;
  2149. }
  2150. return false;
  2151. };
  2152. /**
  2153. Figure out a runtime that supports specified capabilities.
  2154. @method thatCan
  2155. @static
  2156. @param {String|Object} caps Set of capabilities to check
  2157. @param {String} [runtimeOrder] Comma-separated list of runtimes to check against
  2158. @return {String} Usable runtime identifier or null
  2159. */
  2160. Runtime.thatCan = function(caps, runtimeOrder) {
  2161. var types = (runtimeOrder || Runtime.order).split(/\s*,\s*/);
  2162. for (var i in types) {
  2163. if (Runtime.can(types[i], caps)) {
  2164. return types[i];
  2165. }
  2166. }
  2167. return null;
  2168. };
  2169. /**
  2170. Figure out an operational mode for the specified set of capabilities.
  2171. @method getMode
  2172. @static
  2173. @param {Object} modeCaps Set of capabilities that depend on particular runtime mode
  2174. @param {Object} [requiredCaps] Supplied set of capabilities to find operational mode for
  2175. @param {String|Boolean} [defaultMode='browser'] Default mode to use
  2176. @return {String|Boolean} Compatible operational mode
  2177. */
  2178. Runtime.getMode = function(modeCaps, requiredCaps, defaultMode) {
  2179. var mode = null;
  2180. if (Basic.typeOf(defaultMode) === 'undefined') { // only if not specified
  2181. defaultMode = 'browser';
  2182. }
  2183. if (requiredCaps && !Basic.isEmptyObj(modeCaps)) {
  2184. // loop over required caps and check if they do require the same mode
  2185. Basic.each(requiredCaps, function(value, cap) {
  2186. if (modeCaps.hasOwnProperty(cap)) {
  2187. var capMode = modeCaps[cap](value);
  2188. // make sure we always have an array
  2189. if (typeof(capMode) === 'string') {
  2190. capMode = [capMode];
  2191. }
  2192. if (!mode) {
  2193. mode = capMode;
  2194. } else if (!(mode = Basic.arrayIntersect(mode, capMode))) {
  2195. // if cap requires conflicting mode - runtime cannot fulfill required caps
  2196. if (MXI_DEBUG && Env.debug.runtime) {
  2197. Env.log("\t\t%c: %v (conflicting mode requested: %s)", cap, value, capMode);
  2198. }
  2199. return (mode = false);
  2200. }
  2201. }
  2202. if (MXI_DEBUG && Env.debug.runtime) {
  2203. Env.log("\t\t%c: %v (compatible modes: %s)", cap, value, mode);
  2204. }
  2205. });
  2206. if (mode) {
  2207. return Basic.inArray(defaultMode, mode) !== -1 ? defaultMode : mode[0];
  2208. } else if (mode === false) {
  2209. return false;
  2210. }
  2211. }
  2212. return defaultMode;
  2213. };
  2214. /**
  2215. Capability check that always returns true
  2216. @private
  2217. @static
  2218. @return {True}
  2219. */
  2220. Runtime.capTrue = function() {
  2221. return true;
  2222. };
  2223. /**
  2224. Capability check that always returns false
  2225. @private
  2226. @static
  2227. @return {False}
  2228. */
  2229. Runtime.capFalse = function() {
  2230. return false;
  2231. };
  2232. /**
  2233. Evaluate the expression to boolean value and create a function that always returns it.
  2234. @private
  2235. @static
  2236. @param {Mixed} expr Expression to evaluate
  2237. @return {Function} Function returning the result of evaluation
  2238. */
  2239. Runtime.capTest = function(expr) {
  2240. return function() {
  2241. return !!expr;
  2242. };
  2243. };
  2244. return Runtime;
  2245. });
  2246. // Included from: src/javascript/runtime/RuntimeClient.js
  2247. /**
  2248. * RuntimeClient.js
  2249. *
  2250. * Copyright 2013, Moxiecode Systems AB
  2251. * Released under GPL License.
  2252. *
  2253. * License: http://www.plupload.com/license
  2254. * Contributing: http://www.plupload.com/contributing
  2255. */
  2256. define('moxie/runtime/RuntimeClient', [
  2257. 'moxie/core/utils/Env',
  2258. 'moxie/core/Exceptions',
  2259. 'moxie/core/utils/Basic',
  2260. 'moxie/runtime/Runtime'
  2261. ], function(Env, x, Basic, Runtime) {
  2262. /**
  2263. Set of methods and properties, required by a component to acquire ability to connect to a runtime
  2264. @class RuntimeClient
  2265. */
  2266. return function RuntimeClient() {
  2267. var runtime;
  2268. Basic.extend(this, {
  2269. /**
  2270. Connects to the runtime specified by the options. Will either connect to existing runtime or create a new one.
  2271. Increments number of clients connected to the specified runtime.
  2272. @private
  2273. @method connectRuntime
  2274. @param {Mixed} options Can be a runtme uid or a set of key-value pairs defining requirements and pre-requisites
  2275. */
  2276. connectRuntime: function(options) {
  2277. var comp = this, ruid;
  2278. function initialize(items) {
  2279. var type, constructor;
  2280. // if we ran out of runtimes
  2281. if (!items.length) {
  2282. comp.trigger('RuntimeError', new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  2283. runtime = null;
  2284. return;
  2285. }
  2286. type = items.shift().toLowerCase();
  2287. constructor = Runtime.getConstructor(type);
  2288. if (!constructor) {
  2289. initialize(items);
  2290. return;
  2291. }
  2292. if (MXI_DEBUG && Env.debug.runtime) {
  2293. Env.log("Trying runtime: %s", type);
  2294. Env.log(options);
  2295. }
  2296. // try initializing the runtime
  2297. runtime = new constructor(options);
  2298. runtime.bind('Init', function() {
  2299. // mark runtime as initialized
  2300. runtime.initialized = true;
  2301. if (MXI_DEBUG && Env.debug.runtime) {
  2302. Env.log("Runtime '%s' initialized", runtime.type);
  2303. }
  2304. // jailbreak ...
  2305. setTimeout(function() {
  2306. runtime.clients++;
  2307. // this will be triggered on component
  2308. comp.trigger('RuntimeInit', runtime);
  2309. }, 1);
  2310. });
  2311. runtime.bind('Error', function() {
  2312. if (MXI_DEBUG && Env.debug.runtime) {
  2313. Env.log("Runtime '%s' failed to initialize", runtime.type);
  2314. }
  2315. runtime.destroy(); // runtime cannot destroy itself from inside at a right moment, thus we do it here
  2316. initialize(items);
  2317. });
  2318. /*runtime.bind('Exception', function() { });*/
  2319. if (MXI_DEBUG && Env.debug.runtime) {
  2320. Env.log("\tselected mode: %s", runtime.mode);
  2321. }
  2322. // check if runtime managed to pick-up operational mode
  2323. if (!runtime.mode) {
  2324. runtime.trigger('Error');
  2325. return;
  2326. }
  2327. runtime.init();
  2328. }
  2329. // check if a particular runtime was requested
  2330. if (Basic.typeOf(options) === 'string') {
  2331. ruid = options;
  2332. } else if (Basic.typeOf(options.ruid) === 'string') {
  2333. ruid = options.ruid;
  2334. }
  2335. if (ruid) {
  2336. runtime = Runtime.getRuntime(ruid);
  2337. if (runtime) {
  2338. runtime.clients++;
  2339. return runtime;
  2340. } else {
  2341. // there should be a runtime and there's none - weird case
  2342. throw new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR);
  2343. }
  2344. }
  2345. // initialize a fresh one, that fits runtime list and required features best
  2346. initialize((options.runtime_order || Runtime.order).split(/\s*,\s*/));
  2347. },
  2348. /**
  2349. Disconnects from the runtime. Decrements number of clients connected to the specified runtime.
  2350. @private
  2351. @method disconnectRuntime
  2352. */
  2353. disconnectRuntime: function() {
  2354. if (runtime && --runtime.clients <= 0) {
  2355. runtime.destroy();
  2356. }
  2357. // once the component is disconnected, it shouldn't have access to the runtime
  2358. runtime = null;
  2359. },
  2360. /**
  2361. Returns the runtime to which the client is currently connected.
  2362. @method getRuntime
  2363. @return {Runtime} Runtime or null if client is not connected
  2364. */
  2365. getRuntime: function() {
  2366. if (runtime && runtime.uid) {
  2367. return runtime;
  2368. }
  2369. return runtime = null; // make sure we do not leave zombies rambling around
  2370. },
  2371. /**
  2372. Handy shortcut to safely invoke runtime extension methods.
  2373. @private
  2374. @method exec
  2375. @return {Mixed} Whatever runtime extension method returns
  2376. */
  2377. exec: function() {
  2378. if (runtime) {
  2379. return runtime.exec.apply(this, arguments);
  2380. }
  2381. return null;
  2382. }
  2383. });
  2384. };
  2385. });
  2386. // Included from: src/javascript/file/FileInput.js
  2387. /**
  2388. * FileInput.js
  2389. *
  2390. * Copyright 2013, Moxiecode Systems AB
  2391. * Released under GPL License.
  2392. *
  2393. * License: http://www.plupload.com/license
  2394. * Contributing: http://www.plupload.com/contributing
  2395. */
  2396. define('moxie/file/FileInput', [
  2397. 'moxie/core/utils/Basic',
  2398. 'moxie/core/utils/Env',
  2399. 'moxie/core/utils/Mime',
  2400. 'moxie/core/utils/Dom',
  2401. 'moxie/core/Exceptions',
  2402. 'moxie/core/EventTarget',
  2403. 'moxie/core/I18n',
  2404. 'moxie/runtime/Runtime',
  2405. 'moxie/runtime/RuntimeClient'
  2406. ], function(Basic, Env, Mime, Dom, x, EventTarget, I18n, Runtime, RuntimeClient) {
  2407. /**
  2408. Provides a convenient way to create cross-browser file-picker. Generates file selection dialog on click,
  2409. converts selected files to _File_ objects, to be used in conjunction with _Image_, preloaded in memory
  2410. with _FileReader_ or uploaded to a server through _XMLHttpRequest_.
  2411. @class FileInput
  2412. @constructor
  2413. @extends EventTarget
  2414. @uses RuntimeClient
  2415. @param {Object|String|DOMElement} options If options is string or node, argument is considered as _browse\_button_.
  2416. @param {String|DOMElement} options.browse_button DOM Element to turn into file picker.
  2417. @param {Array} [options.accept] Array of mime types to accept. By default accepts all.
  2418. @param {String} [options.file='file'] Name of the file field (not the filename).
  2419. @param {Boolean} [options.multiple=false] Enable selection of multiple files.
  2420. @param {Boolean} [options.directory=false] Turn file input into the folder input (cannot be both at the same time).
  2421. @param {String|DOMElement} [options.container] DOM Element to use as a container for file-picker. Defaults to parentNode
  2422. for _browse\_button_.
  2423. @param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support.
  2424. @example
  2425. <div id="container">
  2426. <a id="file-picker" href="javascript:;">Browse...</a>
  2427. </div>
  2428. <script>
  2429. var fileInput = new mOxie.FileInput({
  2430. browse_button: 'file-picker', // or document.getElementById('file-picker')
  2431. container: 'container',
  2432. accept: [
  2433. {title: "Image files", extensions: "jpg,gif,png"} // accept only images
  2434. ],
  2435. multiple: true // allow multiple file selection
  2436. });
  2437. fileInput.onchange = function(e) {
  2438. // do something to files array
  2439. console.info(e.target.files); // or this.files or fileInput.files
  2440. };
  2441. fileInput.init(); // initialize
  2442. </script>
  2443. */
  2444. var dispatches = [
  2445. /**
  2446. Dispatched when runtime is connected and file-picker is ready to be used.
  2447. @event ready
  2448. @param {Object} event
  2449. */
  2450. 'ready',
  2451. /**
  2452. Dispatched right after [ready](#event_ready) event, and whenever [refresh()](#method_refresh) is invoked.
  2453. Check [corresponding documentation entry](#method_refresh) for more info.
  2454. @event refresh
  2455. @param {Object} event
  2456. */
  2457. /**
  2458. Dispatched when selection of files in the dialog is complete.
  2459. @event change
  2460. @param {Object} event
  2461. */
  2462. 'change',
  2463. 'cancel', // TODO: might be useful
  2464. /**
  2465. Dispatched when mouse cursor enters file-picker area. Can be used to style element
  2466. accordingly.
  2467. @event mouseenter
  2468. @param {Object} event
  2469. */
  2470. 'mouseenter',
  2471. /**
  2472. Dispatched when mouse cursor leaves file-picker area. Can be used to style element
  2473. accordingly.
  2474. @event mouseleave
  2475. @param {Object} event
  2476. */
  2477. 'mouseleave',
  2478. /**
  2479. Dispatched when functional mouse button is pressed on top of file-picker area.
  2480. @event mousedown
  2481. @param {Object} event
  2482. */
  2483. 'mousedown',
  2484. /**
  2485. Dispatched when functional mouse button is released on top of file-picker area.
  2486. @event mouseup
  2487. @param {Object} event
  2488. */
  2489. 'mouseup'
  2490. ];
  2491. function FileInput(options) {
  2492. if (MXI_DEBUG) {
  2493. Env.log("Instantiating FileInput...");
  2494. }
  2495. var self = this,
  2496. container, browseButton, defaults;
  2497. // if flat argument passed it should be browse_button id
  2498. if (Basic.inArray(Basic.typeOf(options), ['string', 'node']) !== -1) {
  2499. options = { browse_button : options };
  2500. }
  2501. // this will help us to find proper default container
  2502. browseButton = Dom.get(options.browse_button);
  2503. if (!browseButton) {
  2504. // browse button is required
  2505. throw new x.DOMException(x.DOMException.NOT_FOUND_ERR);
  2506. }
  2507. // figure out the options
  2508. defaults = {
  2509. accept: [{
  2510. title: I18n.translate('All Files'),
  2511. extensions: '*'
  2512. }],
  2513. name: 'file',
  2514. multiple: false,
  2515. required_caps: false,
  2516. container: browseButton.parentNode || document.body
  2517. };
  2518. options = Basic.extend({}, defaults, options);
  2519. // convert to object representation
  2520. if (typeof(options.required_caps) === 'string') {
  2521. options.required_caps = Runtime.parseCaps(options.required_caps);
  2522. }
  2523. // normalize accept option (could be list of mime types or array of title/extensions pairs)
  2524. if (typeof(options.accept) === 'string') {
  2525. options.accept = Mime.mimes2extList(options.accept);
  2526. }
  2527. container = Dom.get(options.container);
  2528. // make sure we have container
  2529. if (!container) {
  2530. container = document.body;
  2531. }
  2532. // make container relative, if it's not
  2533. if (Dom.getStyle(container, 'position') === 'static') {
  2534. container.style.position = 'relative';
  2535. }
  2536. container = browseButton = null; // IE
  2537. RuntimeClient.call(self);
  2538. Basic.extend(self, {
  2539. /**
  2540. Unique id of the component
  2541. @property uid
  2542. @protected
  2543. @readOnly
  2544. @type {String}
  2545. @default UID
  2546. */
  2547. uid: Basic.guid('uid_'),
  2548. /**
  2549. Unique id of the connected runtime, if any.
  2550. @property ruid
  2551. @protected
  2552. @type {String}
  2553. */
  2554. ruid: null,
  2555. /**
  2556. Unique id of the runtime container. Useful to get hold of it for various manipulations.
  2557. @property shimid
  2558. @protected
  2559. @type {String}
  2560. */
  2561. shimid: null,
  2562. /**
  2563. Array of selected mOxie.File objects
  2564. @property files
  2565. @type {Array}
  2566. @default null
  2567. */
  2568. files: null,
  2569. /**
  2570. Initializes the file-picker, connects it to runtime and dispatches event ready when done.
  2571. @method init
  2572. */
  2573. init: function() {
  2574. self.bind('RuntimeInit', function(e, runtime) {
  2575. self.ruid = runtime.uid;
  2576. self.shimid = runtime.shimid;
  2577. self.bind("Ready", function() {
  2578. self.trigger("Refresh");
  2579. }, 999);
  2580. // re-position and resize shim container
  2581. self.bind('Refresh', function() {
  2582. var pos, size, browseButton, shimContainer;
  2583. browseButton = Dom.get(options.browse_button);
  2584. shimContainer = Dom.get(runtime.shimid); // do not use runtime.getShimContainer(), since it will create container if it doesn't exist
  2585. if (browseButton) {
  2586. pos = Dom.getPos(browseButton, Dom.get(options.container));
  2587. size = Dom.getSize(browseButton);
  2588. if (shimContainer) {
  2589. Basic.extend(shimContainer.style, {
  2590. top : pos.y + 'px',
  2591. left : pos.x + 'px',
  2592. width : size.w + 'px',
  2593. height : size.h + 'px'
  2594. });
  2595. }
  2596. }
  2597. shimContainer = browseButton = null;
  2598. });
  2599. runtime.exec.call(self, 'FileInput', 'init', options);
  2600. });
  2601. // runtime needs: options.required_features, options.runtime_order and options.container
  2602. self.connectRuntime(Basic.extend({}, options, {
  2603. required_caps: {
  2604. select_file: true
  2605. }
  2606. }));
  2607. },
  2608. /**
  2609. Disables file-picker element, so that it doesn't react to mouse clicks.
  2610. @method disable
  2611. @param {Boolean} [state=true] Disable component if - true, enable if - false
  2612. */
  2613. disable: function(state) {
  2614. var runtime = this.getRuntime();
  2615. if (runtime) {
  2616. runtime.exec.call(this, 'FileInput', 'disable', Basic.typeOf(state) === 'undefined' ? true : state);
  2617. }
  2618. },
  2619. /**
  2620. Reposition and resize dialog trigger to match the position and size of browse_button element.
  2621. @method refresh
  2622. */
  2623. refresh: function() {
  2624. self.trigger("Refresh");
  2625. },
  2626. /**
  2627. Destroy component.
  2628. @method destroy
  2629. */
  2630. destroy: function() {
  2631. var runtime = this.getRuntime();
  2632. if (runtime) {
  2633. runtime.exec.call(this, 'FileInput', 'destroy');
  2634. this.disconnectRuntime();
  2635. }
  2636. if (Basic.typeOf(this.files) === 'array') {
  2637. // no sense in leaving associated files behind
  2638. Basic.each(this.files, function(file) {
  2639. file.destroy();
  2640. });
  2641. }
  2642. this.files = null;
  2643. this.unbindAll();
  2644. }
  2645. });
  2646. this.handleEventProps(dispatches);
  2647. }
  2648. FileInput.prototype = EventTarget.instance;
  2649. return FileInput;
  2650. });
  2651. // Included from: src/javascript/core/utils/Encode.js
  2652. /**
  2653. * Encode.js
  2654. *
  2655. * Copyright 2013, Moxiecode Systems AB
  2656. * Released under GPL License.
  2657. *
  2658. * License: http://www.plupload.com/license
  2659. * Contributing: http://www.plupload.com/contributing
  2660. */
  2661. define('moxie/core/utils/Encode', [], function() {
  2662. /**
  2663. Encode string with UTF-8
  2664. @method utf8_encode
  2665. @for Utils
  2666. @static
  2667. @param {String} str String to encode
  2668. @return {String} UTF-8 encoded string
  2669. */
  2670. var utf8_encode = function(str) {
  2671. return unescape(encodeURIComponent(str));
  2672. };
  2673. /**
  2674. Decode UTF-8 encoded string
  2675. @method utf8_decode
  2676. @static
  2677. @param {String} str String to decode
  2678. @return {String} Decoded string
  2679. */
  2680. var utf8_decode = function(str_data) {
  2681. return decodeURIComponent(escape(str_data));
  2682. };
  2683. /**
  2684. Decode Base64 encoded string (uses browser's default method if available),
  2685. from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_decode.js
  2686. @method atob
  2687. @static
  2688. @param {String} data String to decode
  2689. @return {String} Decoded string
  2690. */
  2691. var atob = function(data, utf8) {
  2692. if (typeof(window.atob) === 'function') {
  2693. return utf8 ? utf8_decode(window.atob(data)) : window.atob(data);
  2694. }
  2695. // http://kevin.vanzonneveld.net
  2696. // + original by: Tyler Akins (http://rumkin.com)
  2697. // + improved by: Thunder.m
  2698. // + input by: Aman Gupta
  2699. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2700. // + bugfixed by: Onno Marsman
  2701. // + bugfixed by: Pellentesque Malesuada
  2702. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2703. // + input by: Brett Zamir (http://brett-zamir.me)
  2704. // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2705. // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
  2706. // * returns 1: 'Kevin van Zonneveld'
  2707. // mozilla has this native
  2708. // - but breaks in 2.0.0.12!
  2709. //if (typeof this.window.atob == 'function') {
  2710. // return atob(data);
  2711. //}
  2712. var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  2713. var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
  2714. ac = 0,
  2715. dec = "",
  2716. tmp_arr = [];
  2717. if (!data) {
  2718. return data;
  2719. }
  2720. data += '';
  2721. do { // unpack four hexets into three octets using index points in b64
  2722. h1 = b64.indexOf(data.charAt(i++));
  2723. h2 = b64.indexOf(data.charAt(i++));
  2724. h3 = b64.indexOf(data.charAt(i++));
  2725. h4 = b64.indexOf(data.charAt(i++));
  2726. bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
  2727. o1 = bits >> 16 & 0xff;
  2728. o2 = bits >> 8 & 0xff;
  2729. o3 = bits & 0xff;
  2730. if (h3 == 64) {
  2731. tmp_arr[ac++] = String.fromCharCode(o1);
  2732. } else if (h4 == 64) {
  2733. tmp_arr[ac++] = String.fromCharCode(o1, o2);
  2734. } else {
  2735. tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
  2736. }
  2737. } while (i < data.length);
  2738. dec = tmp_arr.join('');
  2739. return utf8 ? utf8_decode(dec) : dec;
  2740. };
  2741. /**
  2742. Base64 encode string (uses browser's default method if available),
  2743. from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_encode.js
  2744. @method btoa
  2745. @static
  2746. @param {String} data String to encode
  2747. @return {String} Base64 encoded string
  2748. */
  2749. var btoa = function(data, utf8) {
  2750. if (utf8) {
  2751. data = utf8_encode(data);
  2752. }
  2753. if (typeof(window.btoa) === 'function') {
  2754. return window.btoa(data);
  2755. }
  2756. // http://kevin.vanzonneveld.net
  2757. // + original by: Tyler Akins (http://rumkin.com)
  2758. // + improved by: Bayron Guevara
  2759. // + improved by: Thunder.m
  2760. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2761. // + bugfixed by: Pellentesque Malesuada
  2762. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  2763. // + improved by: Rafał Kukawski (http://kukawski.pl)
  2764. // * example 1: base64_encode('Kevin van Zonneveld');
  2765. // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
  2766. // mozilla has this native
  2767. // - but breaks in 2.0.0.12!
  2768. var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  2769. var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
  2770. ac = 0,
  2771. enc = "",
  2772. tmp_arr = [];
  2773. if (!data) {
  2774. return data;
  2775. }
  2776. do { // pack three octets into four hexets
  2777. o1 = data.charCodeAt(i++);
  2778. o2 = data.charCodeAt(i++);
  2779. o3 = data.charCodeAt(i++);
  2780. bits = o1 << 16 | o2 << 8 | o3;
  2781. h1 = bits >> 18 & 0x3f;
  2782. h2 = bits >> 12 & 0x3f;
  2783. h3 = bits >> 6 & 0x3f;
  2784. h4 = bits & 0x3f;
  2785. // use hexets to index into b64, and append result to encoded string
  2786. tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
  2787. } while (i < data.length);
  2788. enc = tmp_arr.join('');
  2789. var r = data.length % 3;
  2790. return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
  2791. };
  2792. return {
  2793. utf8_encode: utf8_encode,
  2794. utf8_decode: utf8_decode,
  2795. atob: atob,
  2796. btoa: btoa
  2797. };
  2798. });
  2799. // Included from: src/javascript/file/Blob.js
  2800. /**
  2801. * Blob.js
  2802. *
  2803. * Copyright 2013, Moxiecode Systems AB
  2804. * Released under GPL License.
  2805. *
  2806. * License: http://www.plupload.com/license
  2807. * Contributing: http://www.plupload.com/contributing
  2808. */
  2809. define('moxie/file/Blob', [
  2810. 'moxie/core/utils/Basic',
  2811. 'moxie/core/utils/Encode',
  2812. 'moxie/runtime/RuntimeClient'
  2813. ], function(Basic, Encode, RuntimeClient) {
  2814. var blobpool = {};
  2815. /**
  2816. @class Blob
  2817. @constructor
  2818. @param {String} ruid Unique id of the runtime, to which this blob belongs to
  2819. @param {Object} blob Object "Native" blob object, as it is represented in the runtime
  2820. */
  2821. function Blob(ruid, blob) {
  2822. function _sliceDetached(start, end, type) {
  2823. var blob, data = blobpool[this.uid];
  2824. if (Basic.typeOf(data) !== 'string' || !data.length) {
  2825. return null; // or throw exception
  2826. }
  2827. blob = new Blob(null, {
  2828. type: type,
  2829. size: end - start
  2830. });
  2831. blob.detach(data.substr(start, blob.size));
  2832. return blob;
  2833. }
  2834. RuntimeClient.call(this);
  2835. if (ruid) {
  2836. this.connectRuntime(ruid);
  2837. }
  2838. if (!blob) {
  2839. blob = {};
  2840. } else if (Basic.typeOf(blob) === 'string') { // dataUrl or binary string
  2841. blob = { data: blob };
  2842. }
  2843. Basic.extend(this, {
  2844. /**
  2845. Unique id of the component
  2846. @property uid
  2847. @type {String}
  2848. */
  2849. uid: blob.uid || Basic.guid('uid_'),
  2850. /**
  2851. Unique id of the connected runtime, if falsy, then runtime will have to be initialized
  2852. before this Blob can be used, modified or sent
  2853. @property ruid
  2854. @type {String}
  2855. */
  2856. ruid: ruid,
  2857. /**
  2858. Size of blob
  2859. @property size
  2860. @type {Number}
  2861. @default 0
  2862. */
  2863. size: blob.size || 0,
  2864. /**
  2865. Mime type of blob
  2866. @property type
  2867. @type {String}
  2868. @default ''
  2869. */
  2870. type: blob.type || '',
  2871. /**
  2872. @method slice
  2873. @param {Number} [start=0]
  2874. */
  2875. slice: function(start, end, type) {
  2876. if (this.isDetached()) {
  2877. return _sliceDetached.apply(this, arguments);
  2878. }
  2879. return this.getRuntime().exec.call(this, 'Blob', 'slice', this.getSource(), start, end, type);
  2880. },
  2881. /**
  2882. Returns "native" blob object (as it is represented in connected runtime) or null if not found
  2883. @method getSource
  2884. @return {Blob} Returns "native" blob object or null if not found
  2885. */
  2886. getSource: function() {
  2887. if (!blobpool[this.uid]) {
  2888. return null;
  2889. }
  2890. return blobpool[this.uid];
  2891. },
  2892. /**
  2893. Detaches blob from any runtime that it depends on and initialize with standalone value
  2894. @method detach
  2895. @protected
  2896. @param {DOMString} [data=''] Standalone value
  2897. */
  2898. detach: function(data) {
  2899. if (this.ruid) {
  2900. this.getRuntime().exec.call(this, 'Blob', 'destroy');
  2901. this.disconnectRuntime();
  2902. this.ruid = null;
  2903. }
  2904. data = data || '';
  2905. // if dataUrl, convert to binary string
  2906. if (data.substr(0, 5) == 'data:') {
  2907. var base64Offset = data.indexOf(';base64,');
  2908. this.type = data.substring(5, base64Offset);
  2909. data = Encode.atob(data.substring(base64Offset + 8));
  2910. }
  2911. this.size = data.length;
  2912. blobpool[this.uid] = data;
  2913. },
  2914. /**
  2915. Checks if blob is standalone (detached of any runtime)
  2916. @method isDetached
  2917. @protected
  2918. @return {Boolean}
  2919. */
  2920. isDetached: function() {
  2921. return !this.ruid && Basic.typeOf(blobpool[this.uid]) === 'string';
  2922. },
  2923. /**
  2924. Destroy Blob and free any resources it was using
  2925. @method destroy
  2926. */
  2927. destroy: function() {
  2928. this.detach();
  2929. delete blobpool[this.uid];
  2930. }
  2931. });
  2932. if (blob.data) {
  2933. this.detach(blob.data); // auto-detach if payload has been passed
  2934. } else {
  2935. blobpool[this.uid] = blob;
  2936. }
  2937. }
  2938. return Blob;
  2939. });
  2940. // Included from: src/javascript/file/File.js
  2941. /**
  2942. * File.js
  2943. *
  2944. * Copyright 2013, Moxiecode Systems AB
  2945. * Released under GPL License.
  2946. *
  2947. * License: http://www.plupload.com/license
  2948. * Contributing: http://www.plupload.com/contributing
  2949. */
  2950. define('moxie/file/File', [
  2951. 'moxie/core/utils/Basic',
  2952. 'moxie/core/utils/Mime',
  2953. 'moxie/file/Blob'
  2954. ], function(Basic, Mime, Blob) {
  2955. /**
  2956. @class File
  2957. @extends Blob
  2958. @constructor
  2959. @param {String} ruid Unique id of the runtime, to which this blob belongs to
  2960. @param {Object} file Object "Native" file object, as it is represented in the runtime
  2961. */
  2962. function File(ruid, file) {
  2963. if (!file) { // avoid extra errors in case we overlooked something
  2964. file = {};
  2965. }
  2966. Blob.apply(this, arguments);
  2967. if (!this.type) {
  2968. this.type = Mime.getFileMime(file.name);
  2969. }
  2970. // sanitize file name or generate new one
  2971. var name;
  2972. if (file.name) {
  2973. name = file.name.replace(/\\/g, '/');
  2974. name = name.substr(name.lastIndexOf('/') + 1);
  2975. } else if (this.type) {
  2976. var prefix = this.type.split('/')[0];
  2977. name = Basic.guid((prefix !== '' ? prefix : 'file') + '_');
  2978. if (Mime.extensions[this.type]) {
  2979. name += '.' + Mime.extensions[this.type][0]; // append proper extension if possible
  2980. }
  2981. }
  2982. Basic.extend(this, {
  2983. /**
  2984. File name
  2985. @property name
  2986. @type {String}
  2987. @default UID
  2988. */
  2989. name: name || Basic.guid('file_'),
  2990. /**
  2991. Relative path to the file inside a directory
  2992. @property relativePath
  2993. @type {String}
  2994. @default ''
  2995. */
  2996. relativePath: '',
  2997. /**
  2998. Date of last modification
  2999. @property lastModifiedDate
  3000. @type {String}
  3001. @default now
  3002. */
  3003. lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString() // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
  3004. });
  3005. }
  3006. File.prototype = Blob.prototype;
  3007. return File;
  3008. });
  3009. // Included from: src/javascript/file/FileDrop.js
  3010. /**
  3011. * FileDrop.js
  3012. *
  3013. * Copyright 2013, Moxiecode Systems AB
  3014. * Released under GPL License.
  3015. *
  3016. * License: http://www.plupload.com/license
  3017. * Contributing: http://www.plupload.com/contributing
  3018. */
  3019. define('moxie/file/FileDrop', [
  3020. 'moxie/core/I18n',
  3021. 'moxie/core/utils/Dom',
  3022. 'moxie/core/Exceptions',
  3023. 'moxie/core/utils/Basic',
  3024. 'moxie/core/utils/Env',
  3025. 'moxie/file/File',
  3026. 'moxie/runtime/RuntimeClient',
  3027. 'moxie/core/EventTarget',
  3028. 'moxie/core/utils/Mime'
  3029. ], function(I18n, Dom, x, Basic, Env, File, RuntimeClient, EventTarget, Mime) {
  3030. /**
  3031. Turn arbitrary DOM element to a drop zone accepting files. Converts selected files to _File_ objects, to be used
  3032. in conjunction with _Image_, preloaded in memory with _FileReader_ or uploaded to a server through
  3033. _XMLHttpRequest_.
  3034. @example
  3035. <div id="drop_zone">
  3036. Drop files here
  3037. </div>
  3038. <br />
  3039. <div id="filelist"></div>
  3040. <script type="text/javascript">
  3041. var fileDrop = new mOxie.FileDrop('drop_zone'), fileList = mOxie.get('filelist');
  3042. fileDrop.ondrop = function() {
  3043. mOxie.each(this.files, function(file) {
  3044. fileList.innerHTML += '<div>' + file.name + '</div>';
  3045. });
  3046. };
  3047. fileDrop.init();
  3048. </script>
  3049. @class FileDrop
  3050. @constructor
  3051. @extends EventTarget
  3052. @uses RuntimeClient
  3053. @param {Object|String} options If options has typeof string, argument is considered as options.drop_zone
  3054. @param {String|DOMElement} options.drop_zone DOM Element to turn into a drop zone
  3055. @param {Array} [options.accept] Array of mime types to accept. By default accepts all
  3056. @param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support
  3057. */
  3058. var dispatches = [
  3059. /**
  3060. Dispatched when runtime is connected and drop zone is ready to accept files.
  3061. @event ready
  3062. @param {Object} event
  3063. */
  3064. 'ready',
  3065. /**
  3066. Dispatched when dragging cursor enters the drop zone.
  3067. @event dragenter
  3068. @param {Object} event
  3069. */
  3070. 'dragenter',
  3071. /**
  3072. Dispatched when dragging cursor leaves the drop zone.
  3073. @event dragleave
  3074. @param {Object} event
  3075. */
  3076. 'dragleave',
  3077. /**
  3078. Dispatched when file is dropped onto the drop zone.
  3079. @event drop
  3080. @param {Object} event
  3081. */
  3082. 'drop',
  3083. /**
  3084. Dispatched if error occurs.
  3085. @event error
  3086. @param {Object} event
  3087. */
  3088. 'error'
  3089. ];
  3090. function FileDrop(options) {
  3091. if (MXI_DEBUG) {
  3092. Env.log("Instantiating FileDrop...");
  3093. }
  3094. var self = this, defaults;
  3095. // if flat argument passed it should be drop_zone id
  3096. if (typeof(options) === 'string') {
  3097. options = { drop_zone : options };
  3098. }
  3099. // figure out the options
  3100. defaults = {
  3101. accept: [{
  3102. title: I18n.translate('All Files'),
  3103. extensions: '*'
  3104. }],
  3105. required_caps: {
  3106. drag_and_drop: true
  3107. }
  3108. };
  3109. options = typeof(options) === 'object' ? Basic.extend({}, defaults, options) : defaults;
  3110. // this will help us to find proper default container
  3111. options.container = Dom.get(options.drop_zone) || document.body;
  3112. // make container relative, if it is not
  3113. if (Dom.getStyle(options.container, 'position') === 'static') {
  3114. options.container.style.position = 'relative';
  3115. }
  3116. // normalize accept option (could be list of mime types or array of title/extensions pairs)
  3117. if (typeof(options.accept) === 'string') {
  3118. options.accept = Mime.mimes2extList(options.accept);
  3119. }
  3120. RuntimeClient.call(self);
  3121. Basic.extend(self, {
  3122. uid: Basic.guid('uid_'),
  3123. ruid: null,
  3124. files: null,
  3125. init: function() {
  3126. self.bind('RuntimeInit', function(e, runtime) {
  3127. self.ruid = runtime.uid;
  3128. runtime.exec.call(self, 'FileDrop', 'init', options);
  3129. self.dispatchEvent('ready');
  3130. });
  3131. // runtime needs: options.required_features, options.runtime_order and options.container
  3132. self.connectRuntime(options); // throws RuntimeError
  3133. },
  3134. destroy: function() {
  3135. var runtime = this.getRuntime();
  3136. if (runtime) {
  3137. runtime.exec.call(this, 'FileDrop', 'destroy');
  3138. this.disconnectRuntime();
  3139. }
  3140. this.files = null;
  3141. this.unbindAll();
  3142. }
  3143. });
  3144. this.handleEventProps(dispatches);
  3145. }
  3146. FileDrop.prototype = EventTarget.instance;
  3147. return FileDrop;
  3148. });
  3149. // Included from: src/javascript/file/FileReader.js
  3150. /**
  3151. * FileReader.js
  3152. *
  3153. * Copyright 2013, Moxiecode Systems AB
  3154. * Released under GPL License.
  3155. *
  3156. * License: http://www.plupload.com/license
  3157. * Contributing: http://www.plupload.com/contributing
  3158. */
  3159. define('moxie/file/FileReader', [
  3160. 'moxie/core/utils/Basic',
  3161. 'moxie/core/utils/Encode',
  3162. 'moxie/core/Exceptions',
  3163. 'moxie/core/EventTarget',
  3164. 'moxie/file/Blob',
  3165. 'moxie/runtime/RuntimeClient'
  3166. ], function(Basic, Encode, x, EventTarget, Blob, RuntimeClient) {
  3167. /**
  3168. Utility for preloading o.Blob/o.File objects in memory. By design closely follows [W3C FileReader](http://www.w3.org/TR/FileAPI/#dfn-filereader)
  3169. interface. Where possible uses native FileReader, where - not falls back to shims.
  3170. @class FileReader
  3171. @constructor FileReader
  3172. @extends EventTarget
  3173. @uses RuntimeClient
  3174. */
  3175. var dispatches = [
  3176. /**
  3177. Dispatched when the read starts.
  3178. @event loadstart
  3179. @param {Object} event
  3180. */
  3181. 'loadstart',
  3182. /**
  3183. Dispatched while reading (and decoding) blob, and reporting partial Blob data (progess.loaded/progress.total).
  3184. @event progress
  3185. @param {Object} event
  3186. */
  3187. 'progress',
  3188. /**
  3189. Dispatched when the read has successfully completed.
  3190. @event load
  3191. @param {Object} event
  3192. */
  3193. 'load',
  3194. /**
  3195. Dispatched when the read has been aborted. For instance, by invoking the abort() method.
  3196. @event abort
  3197. @param {Object} event
  3198. */
  3199. 'abort',
  3200. /**
  3201. Dispatched when the read has failed.
  3202. @event error
  3203. @param {Object} event
  3204. */
  3205. 'error',
  3206. /**
  3207. Dispatched when the request has completed (either in success or failure).
  3208. @event loadend
  3209. @param {Object} event
  3210. */
  3211. 'loadend'
  3212. ];
  3213. function FileReader() {
  3214. RuntimeClient.call(this);
  3215. Basic.extend(this, {
  3216. /**
  3217. UID of the component instance.
  3218. @property uid
  3219. @type {String}
  3220. */
  3221. uid: Basic.guid('uid_'),
  3222. /**
  3223. Contains current state of FileReader object. Can take values of FileReader.EMPTY, FileReader.LOADING
  3224. and FileReader.DONE.
  3225. @property readyState
  3226. @type {Number}
  3227. @default FileReader.EMPTY
  3228. */
  3229. readyState: FileReader.EMPTY,
  3230. /**
  3231. Result of the successful read operation.
  3232. @property result
  3233. @type {String}
  3234. */
  3235. result: null,
  3236. /**
  3237. Stores the error of failed asynchronous read operation.
  3238. @property error
  3239. @type {DOMError}
  3240. */
  3241. error: null,
  3242. /**
  3243. Initiates reading of File/Blob object contents to binary string.
  3244. @method readAsBinaryString
  3245. @param {Blob|File} blob Object to preload
  3246. */
  3247. readAsBinaryString: function(blob) {
  3248. _read.call(this, 'readAsBinaryString', blob);
  3249. },
  3250. /**
  3251. Initiates reading of File/Blob object contents to dataURL string.
  3252. @method readAsDataURL
  3253. @param {Blob|File} blob Object to preload
  3254. */
  3255. readAsDataURL: function(blob) {
  3256. _read.call(this, 'readAsDataURL', blob);
  3257. },
  3258. /**
  3259. Initiates reading of File/Blob object contents to string.
  3260. @method readAsText
  3261. @param {Blob|File} blob Object to preload
  3262. */
  3263. readAsText: function(blob) {
  3264. _read.call(this, 'readAsText', blob);
  3265. },
  3266. /**
  3267. Aborts preloading process.
  3268. @method abort
  3269. */
  3270. abort: function() {
  3271. this.result = null;
  3272. if (Basic.inArray(this.readyState, [FileReader.EMPTY, FileReader.DONE]) !== -1) {
  3273. return;
  3274. } else if (this.readyState === FileReader.LOADING) {
  3275. this.readyState = FileReader.DONE;
  3276. }
  3277. this.exec('FileReader', 'abort');
  3278. this.trigger('abort');
  3279. this.trigger('loadend');
  3280. },
  3281. /**
  3282. Destroy component and release resources.
  3283. @method destroy
  3284. */
  3285. destroy: function() {
  3286. this.abort();
  3287. this.exec('FileReader', 'destroy');
  3288. this.disconnectRuntime();
  3289. this.unbindAll();
  3290. }
  3291. });
  3292. // uid must already be assigned
  3293. this.handleEventProps(dispatches);
  3294. this.bind('Error', function(e, err) {
  3295. this.readyState = FileReader.DONE;
  3296. this.error = err;
  3297. }, 999);
  3298. this.bind('Load', function(e) {
  3299. this.readyState = FileReader.DONE;
  3300. }, 999);
  3301. function _read(op, blob) {
  3302. var self = this;
  3303. this.trigger('loadstart');
  3304. if (this.readyState === FileReader.LOADING) {
  3305. this.trigger('error', new x.DOMException(x.DOMException.INVALID_STATE_ERR));
  3306. this.trigger('loadend');
  3307. return;
  3308. }
  3309. // if source is not o.Blob/o.File
  3310. if (!(blob instanceof Blob)) {
  3311. this.trigger('error', new x.DOMException(x.DOMException.NOT_FOUND_ERR));
  3312. this.trigger('loadend');
  3313. return;
  3314. }
  3315. this.result = null;
  3316. this.readyState = FileReader.LOADING;
  3317. if (blob.isDetached()) {
  3318. var src = blob.getSource();
  3319. switch (op) {
  3320. case 'readAsText':
  3321. case 'readAsBinaryString':
  3322. this.result = src;
  3323. break;
  3324. case 'readAsDataURL':
  3325. this.result = 'data:' + blob.type + ';base64,' + Encode.btoa(src);
  3326. break;
  3327. }
  3328. this.readyState = FileReader.DONE;
  3329. this.trigger('load');
  3330. this.trigger('loadend');
  3331. } else {
  3332. this.connectRuntime(blob.ruid);
  3333. this.exec('FileReader', 'read', op, blob);
  3334. }
  3335. }
  3336. }
  3337. /**
  3338. Initial FileReader state
  3339. @property EMPTY
  3340. @type {Number}
  3341. @final
  3342. @static
  3343. @default 0
  3344. */
  3345. FileReader.EMPTY = 0;
  3346. /**
  3347. FileReader switches to this state when it is preloading the source
  3348. @property LOADING
  3349. @type {Number}
  3350. @final
  3351. @static
  3352. @default 1
  3353. */
  3354. FileReader.LOADING = 1;
  3355. /**
  3356. Preloading is complete, this is a final state
  3357. @property DONE
  3358. @type {Number}
  3359. @final
  3360. @static
  3361. @default 2
  3362. */
  3363. FileReader.DONE = 2;
  3364. FileReader.prototype = EventTarget.instance;
  3365. return FileReader;
  3366. });
  3367. // Included from: src/javascript/core/utils/Url.js
  3368. /**
  3369. * Url.js
  3370. *
  3371. * Copyright 2013, Moxiecode Systems AB
  3372. * Released under GPL License.
  3373. *
  3374. * License: http://www.plupload.com/license
  3375. * Contributing: http://www.plupload.com/contributing
  3376. */
  3377. define('moxie/core/utils/Url', [], function() {
  3378. /**
  3379. Parse url into separate components and fill in absent parts with parts from current url,
  3380. based on https://raw.github.com/kvz/phpjs/master/functions/url/parse_url.js
  3381. @method parseUrl
  3382. @for Utils
  3383. @static
  3384. @param {String} url Url to parse (defaults to empty string if undefined)
  3385. @return {Object} Hash containing extracted uri components
  3386. */
  3387. var parseUrl = function(url, currentUrl) {
  3388. var key = ['source', 'scheme', 'authority', 'userInfo', 'user', 'pass', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment']
  3389. , i = key.length
  3390. , ports = {
  3391. http: 80,
  3392. https: 443
  3393. }
  3394. , uri = {}
  3395. , regex = /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/
  3396. , m = regex.exec(url || '')
  3397. ;
  3398. while (i--) {
  3399. if (m[i]) {
  3400. uri[key[i]] = m[i];
  3401. }
  3402. }
  3403. // when url is relative, we set the origin and the path ourselves
  3404. if (!uri.scheme) {
  3405. // come up with defaults
  3406. if (!currentUrl || typeof(currentUrl) === 'string') {
  3407. currentUrl = parseUrl(currentUrl || document.location.href);
  3408. }
  3409. uri.scheme = currentUrl.scheme;
  3410. uri.host = currentUrl.host;
  3411. uri.port = currentUrl.port;
  3412. var path = '';
  3413. // for urls without trailing slash we need to figure out the path
  3414. if (/^[^\/]/.test(uri.path)) {
  3415. path = currentUrl.path;
  3416. // if path ends with a filename, strip it
  3417. if (/\/[^\/]*\.[^\/]*$/.test(path)) {
  3418. path = path.replace(/\/[^\/]+$/, '/');
  3419. } else {
  3420. // avoid double slash at the end (see #127)
  3421. path = path.replace(/\/?$/, '/');
  3422. }
  3423. }
  3424. uri.path = path + (uri.path || ''); // site may reside at domain.com or domain.com/subdir
  3425. }
  3426. if (!uri.port) {
  3427. uri.port = ports[uri.scheme] || 80;
  3428. }
  3429. uri.port = parseInt(uri.port, 10);
  3430. if (!uri.path) {
  3431. uri.path = "/";
  3432. }
  3433. delete uri.source;
  3434. return uri;
  3435. };
  3436. /**
  3437. Resolve url - among other things will turn relative url to absolute
  3438. @method resolveUrl
  3439. @static
  3440. @param {String|Object} url Either absolute or relative, or a result of parseUrl call
  3441. @return {String} Resolved, absolute url
  3442. */
  3443. var resolveUrl = function(url) {
  3444. var ports = { // we ignore default ports
  3445. http: 80,
  3446. https: 443
  3447. }
  3448. , urlp = typeof(url) === 'object' ? url : parseUrl(url);
  3449. ;
  3450. return urlp.scheme + '://' + urlp.host + (urlp.port !== ports[urlp.scheme] ? ':' + urlp.port : '') + urlp.path + (urlp.query ? urlp.query : '');
  3451. };
  3452. /**
  3453. Check if specified url has the same origin as the current document
  3454. @method hasSameOrigin
  3455. @param {String|Object} url
  3456. @return {Boolean}
  3457. */
  3458. var hasSameOrigin = function(url) {
  3459. function origin(url) {
  3460. return [url.scheme, url.host, url.port].join('/');
  3461. }
  3462. if (typeof url === 'string') {
  3463. url = parseUrl(url);
  3464. }
  3465. return origin(parseUrl()) === origin(url);
  3466. };
  3467. return {
  3468. parseUrl: parseUrl,
  3469. resolveUrl: resolveUrl,
  3470. hasSameOrigin: hasSameOrigin
  3471. };
  3472. });
  3473. // Included from: src/javascript/runtime/RuntimeTarget.js
  3474. /**
  3475. * RuntimeTarget.js
  3476. *
  3477. * Copyright 2013, Moxiecode Systems AB
  3478. * Released under GPL License.
  3479. *
  3480. * License: http://www.plupload.com/license
  3481. * Contributing: http://www.plupload.com/contributing
  3482. */
  3483. define('moxie/runtime/RuntimeTarget', [
  3484. 'moxie/core/utils/Basic',
  3485. 'moxie/runtime/RuntimeClient',
  3486. "moxie/core/EventTarget"
  3487. ], function(Basic, RuntimeClient, EventTarget) {
  3488. /**
  3489. Instance of this class can be used as a target for the events dispatched by shims,
  3490. when allowing them onto components is for either reason inappropriate
  3491. @class RuntimeTarget
  3492. @constructor
  3493. @protected
  3494. @extends EventTarget
  3495. */
  3496. function RuntimeTarget() {
  3497. this.uid = Basic.guid('uid_');
  3498. RuntimeClient.call(this);
  3499. this.destroy = function() {
  3500. this.disconnectRuntime();
  3501. this.unbindAll();
  3502. };
  3503. }
  3504. RuntimeTarget.prototype = EventTarget.instance;
  3505. return RuntimeTarget;
  3506. });
  3507. // Included from: src/javascript/file/FileReaderSync.js
  3508. /**
  3509. * FileReaderSync.js
  3510. *
  3511. * Copyright 2013, Moxiecode Systems AB
  3512. * Released under GPL License.
  3513. *
  3514. * License: http://www.plupload.com/license
  3515. * Contributing: http://www.plupload.com/contributing
  3516. */
  3517. define('moxie/file/FileReaderSync', [
  3518. 'moxie/core/utils/Basic',
  3519. 'moxie/runtime/RuntimeClient',
  3520. 'moxie/core/utils/Encode'
  3521. ], function(Basic, RuntimeClient, Encode) {
  3522. /**
  3523. Synchronous FileReader implementation. Something like this is available in WebWorkers environment, here
  3524. it can be used to read only preloaded blobs/files and only below certain size (not yet sure what that'd be,
  3525. but probably < 1mb). Not meant to be used directly by user.
  3526. @class FileReaderSync
  3527. @private
  3528. @constructor
  3529. */
  3530. return function() {
  3531. RuntimeClient.call(this);
  3532. Basic.extend(this, {
  3533. uid: Basic.guid('uid_'),
  3534. readAsBinaryString: function(blob) {
  3535. return _read.call(this, 'readAsBinaryString', blob);
  3536. },
  3537. readAsDataURL: function(blob) {
  3538. return _read.call(this, 'readAsDataURL', blob);
  3539. },
  3540. /*readAsArrayBuffer: function(blob) {
  3541. return _read.call(this, 'readAsArrayBuffer', blob);
  3542. },*/
  3543. readAsText: function(blob) {
  3544. return _read.call(this, 'readAsText', blob);
  3545. }
  3546. });
  3547. function _read(op, blob) {
  3548. if (blob.isDetached()) {
  3549. var src = blob.getSource();
  3550. switch (op) {
  3551. case 'readAsBinaryString':
  3552. return src;
  3553. case 'readAsDataURL':
  3554. return 'data:' + blob.type + ';base64,' + Encode.btoa(src);
  3555. case 'readAsText':
  3556. var txt = '';
  3557. for (var i = 0, length = src.length; i < length; i++) {
  3558. txt += String.fromCharCode(src[i]);
  3559. }
  3560. return txt;
  3561. }
  3562. } else {
  3563. var result = this.connectRuntime(blob.ruid).exec.call(this, 'FileReaderSync', 'read', op, blob);
  3564. this.disconnectRuntime();
  3565. return result;
  3566. }
  3567. }
  3568. };
  3569. });
  3570. // Included from: src/javascript/xhr/FormData.js
  3571. /**
  3572. * FormData.js
  3573. *
  3574. * Copyright 2013, Moxiecode Systems AB
  3575. * Released under GPL License.
  3576. *
  3577. * License: http://www.plupload.com/license
  3578. * Contributing: http://www.plupload.com/contributing
  3579. */
  3580. define("moxie/xhr/FormData", [
  3581. "moxie/core/Exceptions",
  3582. "moxie/core/utils/Basic",
  3583. "moxie/file/Blob"
  3584. ], function(x, Basic, Blob) {
  3585. /**
  3586. FormData
  3587. @class FormData
  3588. @constructor
  3589. */
  3590. function FormData() {
  3591. var _blob, _fields = [];
  3592. Basic.extend(this, {
  3593. /**
  3594. Append another key-value pair to the FormData object
  3595. @method append
  3596. @param {String} name Name for the new field
  3597. @param {String|Blob|Array|Object} value Value for the field
  3598. */
  3599. append: function(name, value) {
  3600. var self = this, valueType = Basic.typeOf(value);
  3601. // according to specs value might be either Blob or String
  3602. if (value instanceof Blob) {
  3603. _blob = {
  3604. name: name,
  3605. value: value // unfortunately we can only send single Blob in one FormData
  3606. };
  3607. } else if ('array' === valueType) {
  3608. name += '[]';
  3609. Basic.each(value, function(value) {
  3610. self.append(name, value);
  3611. });
  3612. } else if ('object' === valueType) {
  3613. Basic.each(value, function(value, key) {
  3614. self.append(name + '[' + key + ']', value);
  3615. });
  3616. } else if ('null' === valueType || 'undefined' === valueType || 'number' === valueType && isNaN(value)) {
  3617. self.append(name, "false");
  3618. } else {
  3619. _fields.push({
  3620. name: name,
  3621. value: value.toString()
  3622. });
  3623. }
  3624. },
  3625. /**
  3626. Checks if FormData contains Blob.
  3627. @method hasBlob
  3628. @return {Boolean}
  3629. */
  3630. hasBlob: function() {
  3631. return !!this.getBlob();
  3632. },
  3633. /**
  3634. Retrieves blob.
  3635. @method getBlob
  3636. @return {Object} Either Blob if found or null
  3637. */
  3638. getBlob: function() {
  3639. return _blob && _blob.value || null;
  3640. },
  3641. /**
  3642. Retrieves blob field name.
  3643. @method getBlobName
  3644. @return {String} Either Blob field name or null
  3645. */
  3646. getBlobName: function() {
  3647. return _blob && _blob.name || null;
  3648. },
  3649. /**
  3650. Loop over the fields in FormData and invoke the callback for each of them.
  3651. @method each
  3652. @param {Function} cb Callback to call for each field
  3653. */
  3654. each: function(cb) {
  3655. Basic.each(_fields, function(field) {
  3656. cb(field.value, field.name);
  3657. });
  3658. if (_blob) {
  3659. cb(_blob.value, _blob.name);
  3660. }
  3661. },
  3662. destroy: function() {
  3663. _blob = null;
  3664. _fields = [];
  3665. }
  3666. });
  3667. }
  3668. return FormData;
  3669. });
  3670. // Included from: src/javascript/xhr/XMLHttpRequest.js
  3671. /**
  3672. * XMLHttpRequest.js
  3673. *
  3674. * Copyright 2013, Moxiecode Systems AB
  3675. * Released under GPL License.
  3676. *
  3677. * License: http://www.plupload.com/license
  3678. * Contributing: http://www.plupload.com/contributing
  3679. */
  3680. define("moxie/xhr/XMLHttpRequest", [
  3681. "moxie/core/utils/Basic",
  3682. "moxie/core/Exceptions",
  3683. "moxie/core/EventTarget",
  3684. "moxie/core/utils/Encode",
  3685. "moxie/core/utils/Url",
  3686. "moxie/runtime/Runtime",
  3687. "moxie/runtime/RuntimeTarget",
  3688. "moxie/file/Blob",
  3689. "moxie/file/FileReaderSync",
  3690. "moxie/xhr/FormData",
  3691. "moxie/core/utils/Env",
  3692. "moxie/core/utils/Mime"
  3693. ], function(Basic, x, EventTarget, Encode, Url, Runtime, RuntimeTarget, Blob, FileReaderSync, FormData, Env, Mime) {
  3694. var httpCode = {
  3695. 100: 'Continue',
  3696. 101: 'Switching Protocols',
  3697. 102: 'Processing',
  3698. 200: 'OK',
  3699. 201: 'Created',
  3700. 202: 'Accepted',
  3701. 203: 'Non-Authoritative Information',
  3702. 204: 'No Content',
  3703. 205: 'Reset Content',
  3704. 206: 'Partial Content',
  3705. 207: 'Multi-Status',
  3706. 226: 'IM Used',
  3707. 300: 'Multiple Choices',
  3708. 301: 'Moved Permanently',
  3709. 302: 'Found',
  3710. 303: 'See Other',
  3711. 304: 'Not Modified',
  3712. 305: 'Use Proxy',
  3713. 306: 'Reserved',
  3714. 307: 'Temporary Redirect',
  3715. 400: 'Bad Request',
  3716. 401: 'Unauthorized',
  3717. 402: 'Payment Required',
  3718. 403: 'Forbidden',
  3719. 404: 'Not Found',
  3720. 405: 'Method Not Allowed',
  3721. 406: 'Not Acceptable',
  3722. 407: 'Proxy Authentication Required',
  3723. 408: 'Request Timeout',
  3724. 409: 'Conflict',
  3725. 410: 'Gone',
  3726. 411: 'Length Required',
  3727. 412: 'Precondition Failed',
  3728. 413: 'Request Entity Too Large',
  3729. 414: 'Request-URI Too Long',
  3730. 415: 'Unsupported Media Type',
  3731. 416: 'Requested Range Not Satisfiable',
  3732. 417: 'Expectation Failed',
  3733. 422: 'Unprocessable Entity',
  3734. 423: 'Locked',
  3735. 424: 'Failed Dependency',
  3736. 426: 'Upgrade Required',
  3737. 500: 'Internal Server Error',
  3738. 501: 'Not Implemented',
  3739. 502: 'Bad Gateway',
  3740. 503: 'Service Unavailable',
  3741. 504: 'Gateway Timeout',
  3742. 505: 'HTTP Version Not Supported',
  3743. 506: 'Variant Also Negotiates',
  3744. 507: 'Insufficient Storage',
  3745. 510: 'Not Extended'
  3746. };
  3747. function XMLHttpRequestUpload() {
  3748. this.uid = Basic.guid('uid_');
  3749. }
  3750. XMLHttpRequestUpload.prototype = EventTarget.instance;
  3751. /**
  3752. Implementation of XMLHttpRequest
  3753. @class XMLHttpRequest
  3754. @constructor
  3755. @uses RuntimeClient
  3756. @extends EventTarget
  3757. */
  3758. var dispatches = [
  3759. 'loadstart',
  3760. 'progress',
  3761. 'abort',
  3762. 'error',
  3763. 'load',
  3764. 'timeout',
  3765. 'loadend'
  3766. // readystatechange (for historical reasons)
  3767. ];
  3768. var NATIVE = 1, RUNTIME = 2;
  3769. function XMLHttpRequest() {
  3770. var self = this,
  3771. // this (together with _p() @see below) is here to gracefully upgrade to setter/getter syntax where possible
  3772. props = {
  3773. /**
  3774. The amount of milliseconds a request can take before being terminated. Initially zero. Zero means there is no timeout.
  3775. @property timeout
  3776. @type Number
  3777. @default 0
  3778. */
  3779. timeout: 0,
  3780. /**
  3781. Current state, can take following values:
  3782. UNSENT (numeric value 0)
  3783. The object has been constructed.
  3784. OPENED (numeric value 1)
  3785. The open() method has been successfully invoked. During this state request headers can be set using setRequestHeader() and the request can be made using the send() method.
  3786. HEADERS_RECEIVED (numeric value 2)
  3787. All redirects (if any) have been followed and all HTTP headers of the final response have been received. Several response members of the object are now available.
  3788. LOADING (numeric value 3)
  3789. The response entity body is being received.
  3790. DONE (numeric value 4)
  3791. @property readyState
  3792. @type Number
  3793. @default 0 (UNSENT)
  3794. */
  3795. readyState: XMLHttpRequest.UNSENT,
  3796. /**
  3797. True when user credentials are to be included in a cross-origin request. False when they are to be excluded
  3798. in a cross-origin request and when cookies are to be ignored in its response. Initially false.
  3799. @property withCredentials
  3800. @type Boolean
  3801. @default false
  3802. */
  3803. withCredentials: false,
  3804. /**
  3805. Returns the HTTP status code.
  3806. @property status
  3807. @type Number
  3808. @default 0
  3809. */
  3810. status: 0,
  3811. /**
  3812. Returns the HTTP status text.
  3813. @property statusText
  3814. @type String
  3815. */
  3816. statusText: "",
  3817. /**
  3818. Returns the response type. Can be set to change the response type. Values are:
  3819. the empty string (default), "arraybuffer", "blob", "document", "json", and "text".
  3820. @property responseType
  3821. @type String
  3822. */
  3823. responseType: "",
  3824. /**
  3825. Returns the document response entity body.
  3826. Throws an "InvalidStateError" exception if responseType is not the empty string or "document".
  3827. @property responseXML
  3828. @type Document
  3829. */
  3830. responseXML: null,
  3831. /**
  3832. Returns the text response entity body.
  3833. Throws an "InvalidStateError" exception if responseType is not the empty string or "text".
  3834. @property responseText
  3835. @type String
  3836. */
  3837. responseText: null,
  3838. /**
  3839. Returns the response entity body (http://www.w3.org/TR/XMLHttpRequest/#response-entity-body).
  3840. Can become: ArrayBuffer, Blob, Document, JSON, Text
  3841. @property response
  3842. @type Mixed
  3843. */
  3844. response: null
  3845. },
  3846. _async = true,
  3847. _url,
  3848. _method,
  3849. _headers = {},
  3850. _user,
  3851. _password,
  3852. _encoding = null,
  3853. _mimeType = null,
  3854. // flags
  3855. _sync_flag = false,
  3856. _send_flag = false,
  3857. _upload_events_flag = false,
  3858. _upload_complete_flag = false,
  3859. _error_flag = false,
  3860. _same_origin_flag = false,
  3861. // times
  3862. _start_time,
  3863. _timeoutset_time,
  3864. _finalMime = null,
  3865. _finalCharset = null,
  3866. _options = {},
  3867. _xhr,
  3868. _responseHeaders = '',
  3869. _responseHeadersBag
  3870. ;
  3871. Basic.extend(this, props, {
  3872. /**
  3873. Unique id of the component
  3874. @property uid
  3875. @type String
  3876. */
  3877. uid: Basic.guid('uid_'),
  3878. /**
  3879. Target for Upload events
  3880. @property upload
  3881. @type XMLHttpRequestUpload
  3882. */
  3883. upload: new XMLHttpRequestUpload(),
  3884. /**
  3885. Sets the request method, request URL, synchronous flag, request username, and request password.
  3886. Throws a "SyntaxError" exception if one of the following is true:
  3887. method is not a valid HTTP method.
  3888. url cannot be resolved.
  3889. url contains the "user:password" format in the userinfo production.
  3890. Throws a "SecurityError" exception if method is a case-insensitive match for CONNECT, TRACE or TRACK.
  3891. Throws an "InvalidAccessError" exception if one of the following is true:
  3892. Either user or password is passed as argument and the origin of url does not match the XMLHttpRequest origin.
  3893. There is an associated XMLHttpRequest document and either the timeout attribute is not zero,
  3894. the withCredentials attribute is true, or the responseType attribute is not the empty string.
  3895. @method open
  3896. @param {String} method HTTP method to use on request
  3897. @param {String} url URL to request
  3898. @param {Boolean} [async=true] If false request will be done in synchronous manner. Asynchronous by default.
  3899. @param {String} [user] Username to use in HTTP authentication process on server-side
  3900. @param {String} [password] Password to use in HTTP authentication process on server-side
  3901. */
  3902. open: function(method, url, async, user, password) {
  3903. var urlp;
  3904. // first two arguments are required
  3905. if (!method || !url) {
  3906. throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  3907. }
  3908. // 2 - check if any code point in method is higher than U+00FF or after deflating method it does not match the method
  3909. if (/[\u0100-\uffff]/.test(method) || Encode.utf8_encode(method) !== method) {
  3910. throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  3911. }
  3912. // 3
  3913. if (!!~Basic.inArray(method.toUpperCase(), ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'TRACE', 'TRACK'])) {
  3914. _method = method.toUpperCase();
  3915. }
  3916. // 4 - allowing these methods poses a security risk
  3917. if (!!~Basic.inArray(_method, ['CONNECT', 'TRACE', 'TRACK'])) {
  3918. throw new x.DOMException(x.DOMException.SECURITY_ERR);
  3919. }
  3920. // 5
  3921. url = Encode.utf8_encode(url);
  3922. // 6 - Resolve url relative to the XMLHttpRequest base URL. If the algorithm returns an error, throw a "SyntaxError".
  3923. urlp = Url.parseUrl(url);
  3924. _same_origin_flag = Url.hasSameOrigin(urlp);
  3925. // 7 - manually build up absolute url
  3926. _url = Url.resolveUrl(url);
  3927. // 9-10, 12-13
  3928. if ((user || password) && !_same_origin_flag) {
  3929. throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  3930. }
  3931. _user = user || urlp.user;
  3932. _password = password || urlp.pass;
  3933. // 11
  3934. _async = async || true;
  3935. if (_async === false && (_p('timeout') || _p('withCredentials') || _p('responseType') !== "")) {
  3936. throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  3937. }
  3938. // 14 - terminate abort()
  3939. // 15 - terminate send()
  3940. // 18
  3941. _sync_flag = !_async;
  3942. _send_flag = false;
  3943. _headers = {};
  3944. _reset.call(this);
  3945. // 19
  3946. _p('readyState', XMLHttpRequest.OPENED);
  3947. // 20
  3948. this.dispatchEvent('readystatechange');
  3949. },
  3950. /**
  3951. Appends an header to the list of author request headers, or if header is already
  3952. in the list of author request headers, combines its value with value.
  3953. Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set.
  3954. Throws a "SyntaxError" exception if header is not a valid HTTP header field name or if value
  3955. is not a valid HTTP header field value.
  3956. @method setRequestHeader
  3957. @param {String} header
  3958. @param {String|Number} value
  3959. */
  3960. setRequestHeader: function(header, value) {
  3961. var uaHeaders = [ // these headers are controlled by the user agent
  3962. "accept-charset",
  3963. "accept-encoding",
  3964. "access-control-request-headers",
  3965. "access-control-request-method",
  3966. "connection",
  3967. "content-length",
  3968. "cookie",
  3969. "cookie2",
  3970. "content-transfer-encoding",
  3971. "date",
  3972. "expect",
  3973. "host",
  3974. "keep-alive",
  3975. "origin",
  3976. "referer",
  3977. "te",
  3978. "trailer",
  3979. "transfer-encoding",
  3980. "upgrade",
  3981. "user-agent",
  3982. "via"
  3983. ];
  3984. // 1-2
  3985. if (_p('readyState') !== XMLHttpRequest.OPENED || _send_flag) {
  3986. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  3987. }
  3988. // 3
  3989. if (/[\u0100-\uffff]/.test(header) || Encode.utf8_encode(header) !== header) {
  3990. throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  3991. }
  3992. // 4
  3993. /* this step is seemingly bypassed in browsers, probably to allow various unicode characters in header values
  3994. if (/[\u0100-\uffff]/.test(value) || Encode.utf8_encode(value) !== value) {
  3995. throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  3996. }*/
  3997. header = Basic.trim(header).toLowerCase();
  3998. // setting of proxy-* and sec-* headers is prohibited by spec
  3999. if (!!~Basic.inArray(header, uaHeaders) || /^(proxy\-|sec\-)/.test(header)) {
  4000. return false;
  4001. }
  4002. // camelize
  4003. // browsers lowercase header names (at least for custom ones)
  4004. // header = header.replace(/\b\w/g, function($1) { return $1.toUpperCase(); });
  4005. if (!_headers[header]) {
  4006. _headers[header] = value;
  4007. } else {
  4008. // http://tools.ietf.org/html/rfc2616#section-4.2 (last paragraph)
  4009. _headers[header] += ', ' + value;
  4010. }
  4011. return true;
  4012. },
  4013. /**
  4014. Returns all headers from the response, with the exception of those whose field name is Set-Cookie or Set-Cookie2.
  4015. @method getAllResponseHeaders
  4016. @return {String} reponse headers or empty string
  4017. */
  4018. getAllResponseHeaders: function() {
  4019. return _responseHeaders || '';
  4020. },
  4021. /**
  4022. Returns the header field value from the response of which the field name matches header,
  4023. unless the field name is Set-Cookie or Set-Cookie2.
  4024. @method getResponseHeader
  4025. @param {String} header
  4026. @return {String} value(s) for the specified header or null
  4027. */
  4028. getResponseHeader: function(header) {
  4029. header = header.toLowerCase();
  4030. if (_error_flag || !!~Basic.inArray(header, ['set-cookie', 'set-cookie2'])) {
  4031. return null;
  4032. }
  4033. if (_responseHeaders && _responseHeaders !== '') {
  4034. // if we didn't parse response headers until now, do it and keep for later
  4035. if (!_responseHeadersBag) {
  4036. _responseHeadersBag = {};
  4037. Basic.each(_responseHeaders.split(/\r\n/), function(line) {
  4038. var pair = line.split(/:\s+/);
  4039. if (pair.length === 2) { // last line might be empty, omit
  4040. pair[0] = Basic.trim(pair[0]); // just in case
  4041. _responseHeadersBag[pair[0].toLowerCase()] = { // simply to retain header name in original form
  4042. header: pair[0],
  4043. value: Basic.trim(pair[1])
  4044. };
  4045. }
  4046. });
  4047. }
  4048. if (_responseHeadersBag.hasOwnProperty(header)) {
  4049. return _responseHeadersBag[header].header + ': ' + _responseHeadersBag[header].value;
  4050. }
  4051. }
  4052. return null;
  4053. },
  4054. /**
  4055. Sets the Content-Type header for the response to mime.
  4056. Throws an "InvalidStateError" exception if the state is LOADING or DONE.
  4057. Throws a "SyntaxError" exception if mime is not a valid media type.
  4058. @method overrideMimeType
  4059. @param String mime Mime type to set
  4060. */
  4061. overrideMimeType: function(mime) {
  4062. var matches, charset;
  4063. // 1
  4064. if (!!~Basic.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) {
  4065. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4066. }
  4067. // 2
  4068. mime = Basic.trim(mime.toLowerCase());
  4069. if (/;/.test(mime) && (matches = mime.match(/^([^;]+)(?:;\scharset\=)?(.*)$/))) {
  4070. mime = matches[1];
  4071. if (matches[2]) {
  4072. charset = matches[2];
  4073. }
  4074. }
  4075. if (!Mime.mimes[mime]) {
  4076. throw new x.DOMException(x.DOMException.SYNTAX_ERR);
  4077. }
  4078. // 3-4
  4079. _finalMime = mime;
  4080. _finalCharset = charset;
  4081. },
  4082. /**
  4083. Initiates the request. The optional argument provides the request entity body.
  4084. The argument is ignored if request method is GET or HEAD.
  4085. Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set.
  4086. @method send
  4087. @param {Blob|Document|String|FormData} [data] Request entity body
  4088. @param {Object} [options] Set of requirements and pre-requisities for runtime initialization
  4089. */
  4090. send: function(data, options) {
  4091. if (Basic.typeOf(options) === 'string') {
  4092. _options = { ruid: options };
  4093. } else if (!options) {
  4094. _options = {};
  4095. } else {
  4096. _options = options;
  4097. }
  4098. // 1-2
  4099. if (this.readyState !== XMLHttpRequest.OPENED || _send_flag) {
  4100. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4101. }
  4102. // 3
  4103. // sending Blob
  4104. if (data instanceof Blob) {
  4105. _options.ruid = data.ruid;
  4106. _mimeType = data.type || 'application/octet-stream';
  4107. }
  4108. // FormData
  4109. else if (data instanceof FormData) {
  4110. if (data.hasBlob()) {
  4111. var blob = data.getBlob();
  4112. _options.ruid = blob.ruid;
  4113. _mimeType = blob.type || 'application/octet-stream';
  4114. }
  4115. }
  4116. // DOMString
  4117. else if (typeof data === 'string') {
  4118. _encoding = 'UTF-8';
  4119. _mimeType = 'text/plain;charset=UTF-8';
  4120. // data should be converted to Unicode and encoded as UTF-8
  4121. data = Encode.utf8_encode(data);
  4122. }
  4123. // if withCredentials not set, but requested, set it automatically
  4124. if (!this.withCredentials) {
  4125. this.withCredentials = (_options.required_caps && _options.required_caps.send_browser_cookies) && !_same_origin_flag;
  4126. }
  4127. // 4 - storage mutex
  4128. // 5
  4129. _upload_events_flag = (!_sync_flag && this.upload.hasEventListener()); // DSAP
  4130. // 6
  4131. _error_flag = false;
  4132. // 7
  4133. _upload_complete_flag = !data;
  4134. // 8 - Asynchronous steps
  4135. if (!_sync_flag) {
  4136. // 8.1
  4137. _send_flag = true;
  4138. // 8.2
  4139. // this.dispatchEvent('loadstart'); // will be dispatched either by native or runtime xhr
  4140. // 8.3
  4141. //if (!_upload_complete_flag) {
  4142. // this.upload.dispatchEvent('loadstart'); // will be dispatched either by native or runtime xhr
  4143. //}
  4144. }
  4145. // 8.5 - Return the send() method call, but continue running the steps in this algorithm.
  4146. _doXHR.call(this, data);
  4147. },
  4148. /**
  4149. Cancels any network activity.
  4150. @method abort
  4151. */
  4152. abort: function() {
  4153. _error_flag = true;
  4154. _sync_flag = false;
  4155. if (!~Basic.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED, XMLHttpRequest.DONE])) {
  4156. _p('readyState', XMLHttpRequest.DONE);
  4157. _send_flag = false;
  4158. if (_xhr) {
  4159. _xhr.getRuntime().exec.call(_xhr, 'XMLHttpRequest', 'abort', _upload_complete_flag);
  4160. } else {
  4161. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4162. }
  4163. _upload_complete_flag = true;
  4164. } else {
  4165. _p('readyState', XMLHttpRequest.UNSENT);
  4166. }
  4167. },
  4168. destroy: function() {
  4169. if (_xhr) {
  4170. if (Basic.typeOf(_xhr.destroy) === 'function') {
  4171. _xhr.destroy();
  4172. }
  4173. _xhr = null;
  4174. }
  4175. this.unbindAll();
  4176. if (this.upload) {
  4177. this.upload.unbindAll();
  4178. this.upload = null;
  4179. }
  4180. }
  4181. });
  4182. this.handleEventProps(dispatches.concat(['readystatechange'])); // for historical reasons
  4183. this.upload.handleEventProps(dispatches);
  4184. /* this is nice, but maybe too lengthy
  4185. // if supported by JS version, set getters/setters for specific properties
  4186. o.defineProperty(this, 'readyState', {
  4187. configurable: false,
  4188. get: function() {
  4189. return _p('readyState');
  4190. }
  4191. });
  4192. o.defineProperty(this, 'timeout', {
  4193. configurable: false,
  4194. get: function() {
  4195. return _p('timeout');
  4196. },
  4197. set: function(value) {
  4198. if (_sync_flag) {
  4199. throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  4200. }
  4201. // timeout still should be measured relative to the start time of request
  4202. _timeoutset_time = (new Date).getTime();
  4203. _p('timeout', value);
  4204. }
  4205. });
  4206. // the withCredentials attribute has no effect when fetching same-origin resources
  4207. o.defineProperty(this, 'withCredentials', {
  4208. configurable: false,
  4209. get: function() {
  4210. return _p('withCredentials');
  4211. },
  4212. set: function(value) {
  4213. // 1-2
  4214. if (!~o.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED]) || _send_flag) {
  4215. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4216. }
  4217. // 3-4
  4218. if (_anonymous_flag || _sync_flag) {
  4219. throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  4220. }
  4221. // 5
  4222. _p('withCredentials', value);
  4223. }
  4224. });
  4225. o.defineProperty(this, 'status', {
  4226. configurable: false,
  4227. get: function() {
  4228. return _p('status');
  4229. }
  4230. });
  4231. o.defineProperty(this, 'statusText', {
  4232. configurable: false,
  4233. get: function() {
  4234. return _p('statusText');
  4235. }
  4236. });
  4237. o.defineProperty(this, 'responseType', {
  4238. configurable: false,
  4239. get: function() {
  4240. return _p('responseType');
  4241. },
  4242. set: function(value) {
  4243. // 1
  4244. if (!!~o.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) {
  4245. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4246. }
  4247. // 2
  4248. if (_sync_flag) {
  4249. throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
  4250. }
  4251. // 3
  4252. _p('responseType', value.toLowerCase());
  4253. }
  4254. });
  4255. o.defineProperty(this, 'responseText', {
  4256. configurable: false,
  4257. get: function() {
  4258. // 1
  4259. if (!~o.inArray(_p('responseType'), ['', 'text'])) {
  4260. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4261. }
  4262. // 2-3
  4263. if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) {
  4264. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4265. }
  4266. return _p('responseText');
  4267. }
  4268. });
  4269. o.defineProperty(this, 'responseXML', {
  4270. configurable: false,
  4271. get: function() {
  4272. // 1
  4273. if (!~o.inArray(_p('responseType'), ['', 'document'])) {
  4274. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4275. }
  4276. // 2-3
  4277. if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) {
  4278. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4279. }
  4280. return _p('responseXML');
  4281. }
  4282. });
  4283. o.defineProperty(this, 'response', {
  4284. configurable: false,
  4285. get: function() {
  4286. if (!!~o.inArray(_p('responseType'), ['', 'text'])) {
  4287. if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) {
  4288. return '';
  4289. }
  4290. }
  4291. if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) {
  4292. return null;
  4293. }
  4294. return _p('response');
  4295. }
  4296. });
  4297. */
  4298. function _p(prop, value) {
  4299. if (!props.hasOwnProperty(prop)) {
  4300. return;
  4301. }
  4302. if (arguments.length === 1) { // get
  4303. return Env.can('define_property') ? props[prop] : self[prop];
  4304. } else { // set
  4305. if (Env.can('define_property')) {
  4306. props[prop] = value;
  4307. } else {
  4308. self[prop] = value;
  4309. }
  4310. }
  4311. }
  4312. /*
  4313. function _toASCII(str, AllowUnassigned, UseSTD3ASCIIRules) {
  4314. // TODO: http://tools.ietf.org/html/rfc3490#section-4.1
  4315. return str.toLowerCase();
  4316. }
  4317. */
  4318. function _doXHR(data) {
  4319. var self = this;
  4320. _start_time = new Date().getTime();
  4321. _xhr = new RuntimeTarget();
  4322. function loadEnd() {
  4323. if (_xhr) { // it could have been destroyed by now
  4324. _xhr.destroy();
  4325. _xhr = null;
  4326. }
  4327. self.dispatchEvent('loadend');
  4328. self = null;
  4329. }
  4330. function exec(runtime) {
  4331. _xhr.bind('LoadStart', function(e) {
  4332. _p('readyState', XMLHttpRequest.LOADING);
  4333. self.dispatchEvent('readystatechange');
  4334. self.dispatchEvent(e);
  4335. if (_upload_events_flag) {
  4336. self.upload.dispatchEvent(e);
  4337. }
  4338. });
  4339. _xhr.bind('Progress', function(e) {
  4340. if (_p('readyState') !== XMLHttpRequest.LOADING) {
  4341. _p('readyState', XMLHttpRequest.LOADING); // LoadStart unreliable (in Flash for example)
  4342. self.dispatchEvent('readystatechange');
  4343. }
  4344. self.dispatchEvent(e);
  4345. });
  4346. _xhr.bind('UploadProgress', function(e) {
  4347. if (_upload_events_flag) {
  4348. self.upload.dispatchEvent({
  4349. type: 'progress',
  4350. lengthComputable: false,
  4351. total: e.total,
  4352. loaded: e.loaded
  4353. });
  4354. }
  4355. });
  4356. _xhr.bind('Load', function(e) {
  4357. _p('readyState', XMLHttpRequest.DONE);
  4358. _p('status', Number(runtime.exec.call(_xhr, 'XMLHttpRequest', 'getStatus') || 0));
  4359. _p('statusText', httpCode[_p('status')] || "");
  4360. _p('response', runtime.exec.call(_xhr, 'XMLHttpRequest', 'getResponse', _p('responseType')));
  4361. if (!!~Basic.inArray(_p('responseType'), ['text', ''])) {
  4362. _p('responseText', _p('response'));
  4363. } else if (_p('responseType') === 'document') {
  4364. _p('responseXML', _p('response'));
  4365. }
  4366. _responseHeaders = runtime.exec.call(_xhr, 'XMLHttpRequest', 'getAllResponseHeaders');
  4367. self.dispatchEvent('readystatechange');
  4368. if (_p('status') > 0) { // status 0 usually means that server is unreachable
  4369. if (_upload_events_flag) {
  4370. self.upload.dispatchEvent(e);
  4371. }
  4372. self.dispatchEvent(e);
  4373. } else {
  4374. _error_flag = true;
  4375. self.dispatchEvent('error');
  4376. }
  4377. loadEnd();
  4378. });
  4379. _xhr.bind('Abort', function(e) {
  4380. self.dispatchEvent(e);
  4381. loadEnd();
  4382. });
  4383. _xhr.bind('Error', function(e) {
  4384. _error_flag = true;
  4385. _p('readyState', XMLHttpRequest.DONE);
  4386. self.dispatchEvent('readystatechange');
  4387. _upload_complete_flag = true;
  4388. self.dispatchEvent(e);
  4389. loadEnd();
  4390. });
  4391. runtime.exec.call(_xhr, 'XMLHttpRequest', 'send', {
  4392. url: _url,
  4393. method: _method,
  4394. async: _async,
  4395. user: _user,
  4396. password: _password,
  4397. headers: _headers,
  4398. mimeType: _mimeType,
  4399. encoding: _encoding,
  4400. responseType: self.responseType,
  4401. withCredentials: self.withCredentials,
  4402. options: _options
  4403. }, data);
  4404. }
  4405. // clarify our requirements
  4406. if (typeof(_options.required_caps) === 'string') {
  4407. _options.required_caps = Runtime.parseCaps(_options.required_caps);
  4408. }
  4409. _options.required_caps = Basic.extend({}, _options.required_caps, {
  4410. return_response_type: self.responseType
  4411. });
  4412. if (data instanceof FormData) {
  4413. _options.required_caps.send_multipart = true;
  4414. }
  4415. if (!Basic.isEmptyObj(_headers)) {
  4416. _options.required_caps.send_custom_headers = true;
  4417. }
  4418. if (!_same_origin_flag) {
  4419. _options.required_caps.do_cors = true;
  4420. }
  4421. if (_options.ruid) { // we do not need to wait if we can connect directly
  4422. exec(_xhr.connectRuntime(_options));
  4423. } else {
  4424. _xhr.bind('RuntimeInit', function(e, runtime) {
  4425. exec(runtime);
  4426. });
  4427. _xhr.bind('RuntimeError', function(e, err) {
  4428. self.dispatchEvent('RuntimeError', err);
  4429. });
  4430. _xhr.connectRuntime(_options);
  4431. }
  4432. }
  4433. function _reset() {
  4434. _p('responseText', "");
  4435. _p('responseXML', null);
  4436. _p('response', null);
  4437. _p('status', 0);
  4438. _p('statusText', "");
  4439. _start_time = _timeoutset_time = null;
  4440. }
  4441. }
  4442. XMLHttpRequest.UNSENT = 0;
  4443. XMLHttpRequest.OPENED = 1;
  4444. XMLHttpRequest.HEADERS_RECEIVED = 2;
  4445. XMLHttpRequest.LOADING = 3;
  4446. XMLHttpRequest.DONE = 4;
  4447. XMLHttpRequest.prototype = EventTarget.instance;
  4448. return XMLHttpRequest;
  4449. });
  4450. // Included from: src/javascript/runtime/Transporter.js
  4451. /**
  4452. * Transporter.js
  4453. *
  4454. * Copyright 2013, Moxiecode Systems AB
  4455. * Released under GPL License.
  4456. *
  4457. * License: http://www.plupload.com/license
  4458. * Contributing: http://www.plupload.com/contributing
  4459. */
  4460. define("moxie/runtime/Transporter", [
  4461. "moxie/core/utils/Basic",
  4462. "moxie/core/utils/Encode",
  4463. "moxie/runtime/RuntimeClient",
  4464. "moxie/core/EventTarget"
  4465. ], function(Basic, Encode, RuntimeClient, EventTarget) {
  4466. function Transporter() {
  4467. var mod, _runtime, _data, _size, _pos, _chunk_size;
  4468. RuntimeClient.call(this);
  4469. Basic.extend(this, {
  4470. uid: Basic.guid('uid_'),
  4471. state: Transporter.IDLE,
  4472. result: null,
  4473. transport: function(data, type, options) {
  4474. var self = this;
  4475. options = Basic.extend({
  4476. chunk_size: 204798
  4477. }, options);
  4478. // should divide by three, base64 requires this
  4479. if ((mod = options.chunk_size % 3)) {
  4480. options.chunk_size += 3 - mod;
  4481. }
  4482. _chunk_size = options.chunk_size;
  4483. _reset.call(this);
  4484. _data = data;
  4485. _size = data.length;
  4486. if (Basic.typeOf(options) === 'string' || options.ruid) {
  4487. _run.call(self, type, this.connectRuntime(options));
  4488. } else {
  4489. // we require this to run only once
  4490. var cb = function(e, runtime) {
  4491. self.unbind("RuntimeInit", cb);
  4492. _run.call(self, type, runtime);
  4493. };
  4494. this.bind("RuntimeInit", cb);
  4495. this.connectRuntime(options);
  4496. }
  4497. },
  4498. abort: function() {
  4499. var self = this;
  4500. self.state = Transporter.IDLE;
  4501. if (_runtime) {
  4502. _runtime.exec.call(self, 'Transporter', 'clear');
  4503. self.trigger("TransportingAborted");
  4504. }
  4505. _reset.call(self);
  4506. },
  4507. destroy: function() {
  4508. this.unbindAll();
  4509. _runtime = null;
  4510. this.disconnectRuntime();
  4511. _reset.call(this);
  4512. }
  4513. });
  4514. function _reset() {
  4515. _size = _pos = 0;
  4516. _data = this.result = null;
  4517. }
  4518. function _run(type, runtime) {
  4519. var self = this;
  4520. _runtime = runtime;
  4521. //self.unbind("RuntimeInit");
  4522. self.bind("TransportingProgress", function(e) {
  4523. _pos = e.loaded;
  4524. if (_pos < _size && Basic.inArray(self.state, [Transporter.IDLE, Transporter.DONE]) === -1) {
  4525. _transport.call(self);
  4526. }
  4527. }, 999);
  4528. self.bind("TransportingComplete", function() {
  4529. _pos = _size;
  4530. self.state = Transporter.DONE;
  4531. _data = null; // clean a bit
  4532. self.result = _runtime.exec.call(self, 'Transporter', 'getAsBlob', type || '');
  4533. }, 999);
  4534. self.state = Transporter.BUSY;
  4535. self.trigger("TransportingStarted");
  4536. _transport.call(self);
  4537. }
  4538. function _transport() {
  4539. var self = this,
  4540. chunk,
  4541. bytesLeft = _size - _pos;
  4542. if (_chunk_size > bytesLeft) {
  4543. _chunk_size = bytesLeft;
  4544. }
  4545. chunk = Encode.btoa(_data.substr(_pos, _chunk_size));
  4546. _runtime.exec.call(self, 'Transporter', 'receive', chunk, _size);
  4547. }
  4548. }
  4549. Transporter.IDLE = 0;
  4550. Transporter.BUSY = 1;
  4551. Transporter.DONE = 2;
  4552. Transporter.prototype = EventTarget.instance;
  4553. return Transporter;
  4554. });
  4555. // Included from: src/javascript/image/Image.js
  4556. /**
  4557. * Image.js
  4558. *
  4559. * Copyright 2013, Moxiecode Systems AB
  4560. * Released under GPL License.
  4561. *
  4562. * License: http://www.plupload.com/license
  4563. * Contributing: http://www.plupload.com/contributing
  4564. */
  4565. define("moxie/image/Image", [
  4566. "moxie/core/utils/Basic",
  4567. "moxie/core/utils/Dom",
  4568. "moxie/core/Exceptions",
  4569. "moxie/file/FileReaderSync",
  4570. "moxie/xhr/XMLHttpRequest",
  4571. "moxie/runtime/Runtime",
  4572. "moxie/runtime/RuntimeClient",
  4573. "moxie/runtime/Transporter",
  4574. "moxie/core/utils/Env",
  4575. "moxie/core/EventTarget",
  4576. "moxie/file/Blob",
  4577. "moxie/file/File",
  4578. "moxie/core/utils/Encode"
  4579. ], function(Basic, Dom, x, FileReaderSync, XMLHttpRequest, Runtime, RuntimeClient, Transporter, Env, EventTarget, Blob, File, Encode) {
  4580. /**
  4581. Image preloading and manipulation utility. Additionally it provides access to image meta info (Exif, GPS) and raw binary data.
  4582. @class Image
  4583. @constructor
  4584. @extends EventTarget
  4585. */
  4586. var dispatches = [
  4587. 'progress',
  4588. /**
  4589. Dispatched when loading is complete.
  4590. @event load
  4591. @param {Object} event
  4592. */
  4593. 'load',
  4594. 'error',
  4595. /**
  4596. Dispatched when resize operation is complete.
  4597. @event resize
  4598. @param {Object} event
  4599. */
  4600. 'resize',
  4601. /**
  4602. Dispatched when visual representation of the image is successfully embedded
  4603. into the corresponsing container.
  4604. @event embedded
  4605. @param {Object} event
  4606. */
  4607. 'embedded'
  4608. ];
  4609. function Image() {
  4610. RuntimeClient.call(this);
  4611. Basic.extend(this, {
  4612. /**
  4613. Unique id of the component
  4614. @property uid
  4615. @type {String}
  4616. */
  4617. uid: Basic.guid('uid_'),
  4618. /**
  4619. Unique id of the connected runtime, if any.
  4620. @property ruid
  4621. @type {String}
  4622. */
  4623. ruid: null,
  4624. /**
  4625. Name of the file, that was used to create an image, if available. If not equals to empty string.
  4626. @property name
  4627. @type {String}
  4628. @default ""
  4629. */
  4630. name: "",
  4631. /**
  4632. Size of the image in bytes. Actual value is set only after image is preloaded.
  4633. @property size
  4634. @type {Number}
  4635. @default 0
  4636. */
  4637. size: 0,
  4638. /**
  4639. Width of the image. Actual value is set only after image is preloaded.
  4640. @property width
  4641. @type {Number}
  4642. @default 0
  4643. */
  4644. width: 0,
  4645. /**
  4646. Height of the image. Actual value is set only after image is preloaded.
  4647. @property height
  4648. @type {Number}
  4649. @default 0
  4650. */
  4651. height: 0,
  4652. /**
  4653. Mime type of the image. Currently only image/jpeg and image/png are supported. Actual value is set only after image is preloaded.
  4654. @property type
  4655. @type {String}
  4656. @default ""
  4657. */
  4658. type: "",
  4659. /**
  4660. Holds meta info (Exif, GPS). Is populated only for image/jpeg. Actual value is set only after image is preloaded.
  4661. @property meta
  4662. @type {Object}
  4663. @default {}
  4664. */
  4665. meta: {},
  4666. /**
  4667. Alias for load method, that takes another mOxie.Image object as a source (see load).
  4668. @method clone
  4669. @param {Image} src Source for the image
  4670. @param {Boolean} [exact=false] Whether to activate in-depth clone mode
  4671. */
  4672. clone: function() {
  4673. this.load.apply(this, arguments);
  4674. },
  4675. /**
  4676. Loads image from various sources. Currently the source for new image can be: mOxie.Image, mOxie.Blob/mOxie.File,
  4677. native Blob/File, dataUrl or URL. Depending on the type of the source, arguments - differ. When source is URL,
  4678. Image will be downloaded from remote destination and loaded in memory.
  4679. @example
  4680. var img = new mOxie.Image();
  4681. img.onload = function() {
  4682. var blob = img.getAsBlob();
  4683. var formData = new mOxie.FormData();
  4684. formData.append('file', blob);
  4685. var xhr = new mOxie.XMLHttpRequest();
  4686. xhr.onload = function() {
  4687. // upload complete
  4688. };
  4689. xhr.open('post', 'upload.php');
  4690. xhr.send(formData);
  4691. };
  4692. img.load("http://www.moxiecode.com/images/mox-logo.jpg"); // notice file extension (.jpg)
  4693. @method load
  4694. @param {Image|Blob|File|String} src Source for the image
  4695. @param {Boolean|Object} [mixed]
  4696. */
  4697. load: function() {
  4698. _load.apply(this, arguments);
  4699. },
  4700. /**
  4701. Downsizes the image to fit the specified width/height. If crop is supplied, image will be cropped to exact dimensions.
  4702. @method downsize
  4703. @param {Object} opts
  4704. @param {Number} opts.width Resulting width
  4705. @param {Number} [opts.height=width] Resulting height (optional, if not supplied will default to width)
  4706. @param {Boolean} [opts.crop=false] Whether to crop the image to exact dimensions
  4707. @param {Boolean} [opts.preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
  4708. @param {String} [opts.resample=false] Resampling algorithm to use for resizing
  4709. */
  4710. downsize: function(opts) {
  4711. var defaults = {
  4712. width: this.width,
  4713. height: this.height,
  4714. type: this.type || 'image/jpeg',
  4715. quality: 90,
  4716. crop: false,
  4717. preserveHeaders: true,
  4718. resample: false
  4719. };
  4720. if (typeof(opts) === 'object') {
  4721. opts = Basic.extend(defaults, opts);
  4722. } else {
  4723. // for backward compatibility
  4724. opts = Basic.extend(defaults, {
  4725. width: arguments[0],
  4726. height: arguments[1],
  4727. crop: arguments[2],
  4728. preserveHeaders: arguments[3]
  4729. });
  4730. }
  4731. try {
  4732. if (!this.size) { // only preloaded image objects can be used as source
  4733. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4734. }
  4735. // no way to reliably intercept the crash due to high resolution, so we simply avoid it
  4736. if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
  4737. throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
  4738. }
  4739. this.exec('Image', 'downsize', opts.width, opts.height, opts.crop, opts.preserveHeaders);
  4740. } catch(ex) {
  4741. // for now simply trigger error event
  4742. this.trigger('error', ex.code);
  4743. }
  4744. },
  4745. /**
  4746. Alias for downsize(width, height, true). (see downsize)
  4747. @method crop
  4748. @param {Number} width Resulting width
  4749. @param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
  4750. @param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
  4751. */
  4752. crop: function(width, height, preserveHeaders) {
  4753. this.downsize(width, height, true, preserveHeaders);
  4754. },
  4755. getAsCanvas: function() {
  4756. if (!Env.can('create_canvas')) {
  4757. throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  4758. }
  4759. var runtime = this.connectRuntime(this.ruid);
  4760. return runtime.exec.call(this, 'Image', 'getAsCanvas');
  4761. },
  4762. /**
  4763. Retrieves image in it's current state as mOxie.Blob object. Cannot be run on empty or image in progress (throws
  4764. DOMException.INVALID_STATE_ERR).
  4765. @method getAsBlob
  4766. @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  4767. @param {Number} [quality=90] Applicable only together with mime type image/jpeg
  4768. @return {Blob} Image as Blob
  4769. */
  4770. getAsBlob: function(type, quality) {
  4771. if (!this.size) {
  4772. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4773. }
  4774. return this.exec('Image', 'getAsBlob', type || 'image/jpeg', quality || 90);
  4775. },
  4776. /**
  4777. Retrieves image in it's current state as dataURL string. Cannot be run on empty or image in progress (throws
  4778. DOMException.INVALID_STATE_ERR).
  4779. @method getAsDataURL
  4780. @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  4781. @param {Number} [quality=90] Applicable only together with mime type image/jpeg
  4782. @return {String} Image as dataURL string
  4783. */
  4784. getAsDataURL: function(type, quality) {
  4785. if (!this.size) {
  4786. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4787. }
  4788. return this.exec('Image', 'getAsDataURL', type || 'image/jpeg', quality || 90);
  4789. },
  4790. /**
  4791. Retrieves image in it's current state as binary string. Cannot be run on empty or image in progress (throws
  4792. DOMException.INVALID_STATE_ERR).
  4793. @method getAsBinaryString
  4794. @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
  4795. @param {Number} [quality=90] Applicable only together with mime type image/jpeg
  4796. @return {String} Image as binary string
  4797. */
  4798. getAsBinaryString: function(type, quality) {
  4799. var dataUrl = this.getAsDataURL(type, quality);
  4800. return Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7));
  4801. },
  4802. /**
  4803. Embeds a visual representation of the image into the specified node. Depending on the runtime,
  4804. it might be a canvas, an img node or a thrid party shim object (Flash or SilverLight - very rare,
  4805. can be used in legacy browsers that do not have canvas or proper dataURI support).
  4806. @method embed
  4807. @param {DOMElement} el DOM element to insert the image object into
  4808. @param {Object} [opts]
  4809. @param {Number} [opts.width] The width of an embed (defaults to the image width)
  4810. @param {Number} [opts.height] The height of an embed (defaults to the image height)
  4811. @param {String} [type="image/jpeg"] Mime type
  4812. @param {Number} [quality=90] Quality of an embed, if mime type is image/jpeg
  4813. @param {Boolean} [crop=false] Whether to crop an embed to the specified dimensions
  4814. */
  4815. embed: function(el, opts) {
  4816. var self = this
  4817. , runtime // this has to be outside of all the closures to contain proper runtime
  4818. ;
  4819. opts = Basic.extend({
  4820. width: this.width,
  4821. height: this.height,
  4822. type: this.type || 'image/jpeg',
  4823. quality: 90
  4824. }, opts || {});
  4825. function render(type, quality) {
  4826. var img = this;
  4827. // if possible, embed a canvas element directly
  4828. if (Env.can('create_canvas')) {
  4829. var canvas = img.getAsCanvas();
  4830. if (canvas) {
  4831. el.appendChild(canvas);
  4832. canvas = null;
  4833. img.destroy();
  4834. self.trigger('embedded');
  4835. return;
  4836. }
  4837. }
  4838. var dataUrl = img.getAsDataURL(type, quality);
  4839. if (!dataUrl) {
  4840. throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  4841. }
  4842. if (Env.can('use_data_uri_of', dataUrl.length)) {
  4843. el.innerHTML = '<img src="' + dataUrl + '" width="' + img.width + '" height="' + img.height + '" />';
  4844. img.destroy();
  4845. self.trigger('embedded');
  4846. } else {
  4847. var tr = new Transporter();
  4848. tr.bind("TransportingComplete", function() {
  4849. runtime = self.connectRuntime(this.result.ruid);
  4850. self.bind("Embedded", function() {
  4851. // position and size properly
  4852. Basic.extend(runtime.getShimContainer().style, {
  4853. //position: 'relative',
  4854. top: '0px',
  4855. left: '0px',
  4856. width: img.width + 'px',
  4857. height: img.height + 'px'
  4858. });
  4859. // some shims (Flash/SilverLight) reinitialize, if parent element is hidden, reordered or it's
  4860. // position type changes (in Gecko), but since we basically need this only in IEs 6/7 and
  4861. // sometimes 8 and they do not have this problem, we can comment this for now
  4862. /*tr.bind("RuntimeInit", function(e, runtime) {
  4863. tr.destroy();
  4864. runtime.destroy();
  4865. onResize.call(self); // re-feed our image data
  4866. });*/
  4867. runtime = null; // release
  4868. }, 999);
  4869. runtime.exec.call(self, "ImageView", "display", this.result.uid, width, height);
  4870. img.destroy();
  4871. });
  4872. tr.transport(Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)), type, {
  4873. required_caps: {
  4874. display_media: true
  4875. },
  4876. runtime_order: 'flash,silverlight',
  4877. container: el
  4878. });
  4879. }
  4880. }
  4881. try {
  4882. if (!(el = Dom.get(el))) {
  4883. throw new x.DOMException(x.DOMException.INVALID_NODE_TYPE_ERR);
  4884. }
  4885. if (!this.size) { // only preloaded image objects can be used as source
  4886. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4887. }
  4888. // high-resolution images cannot be consistently handled across the runtimes
  4889. if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
  4890. //throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
  4891. }
  4892. var imgCopy = new Image();
  4893. imgCopy.bind("Resize", function() {
  4894. render.call(this, opts.type, opts.quality);
  4895. });
  4896. imgCopy.bind("Load", function() {
  4897. imgCopy.downsize(opts);
  4898. });
  4899. // if embedded thumb data is available and dimensions are big enough, use it
  4900. if (this.meta.thumb && this.meta.thumb.width >= opts.width && this.meta.thumb.height >= opts.height) {
  4901. imgCopy.load(this.meta.thumb.data);
  4902. } else {
  4903. imgCopy.clone(this, false);
  4904. }
  4905. return imgCopy;
  4906. } catch(ex) {
  4907. // for now simply trigger error event
  4908. this.trigger('error', ex.code);
  4909. }
  4910. },
  4911. /**
  4912. Properly destroys the image and frees resources in use. If any. Recommended way to dispose mOxie.Image object.
  4913. @method destroy
  4914. */
  4915. destroy: function() {
  4916. if (this.ruid) {
  4917. this.getRuntime().exec.call(this, 'Image', 'destroy');
  4918. this.disconnectRuntime();
  4919. }
  4920. this.unbindAll();
  4921. }
  4922. });
  4923. // this is here, because in order to bind properly, we need uid, which is created above
  4924. this.handleEventProps(dispatches);
  4925. this.bind('Load Resize', function() {
  4926. _updateInfo.call(this);
  4927. }, 999);
  4928. function _updateInfo(info) {
  4929. if (!info) {
  4930. info = this.exec('Image', 'getInfo');
  4931. }
  4932. this.size = info.size;
  4933. this.width = info.width;
  4934. this.height = info.height;
  4935. this.type = info.type;
  4936. this.meta = info.meta;
  4937. // update file name, only if empty
  4938. if (this.name === '') {
  4939. this.name = info.name;
  4940. }
  4941. }
  4942. function _load(src) {
  4943. var srcType = Basic.typeOf(src);
  4944. try {
  4945. // if source is Image
  4946. if (src instanceof Image) {
  4947. if (!src.size) { // only preloaded image objects can be used as source
  4948. throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
  4949. }
  4950. _loadFromImage.apply(this, arguments);
  4951. }
  4952. // if source is o.Blob/o.File
  4953. else if (src instanceof Blob) {
  4954. if (!~Basic.inArray(src.type, ['image/jpeg', 'image/png'])) {
  4955. throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  4956. }
  4957. _loadFromBlob.apply(this, arguments);
  4958. }
  4959. // if native blob/file
  4960. else if (Basic.inArray(srcType, ['blob', 'file']) !== -1) {
  4961. _load.call(this, new File(null, src), arguments[1]);
  4962. }
  4963. // if String
  4964. else if (srcType === 'string') {
  4965. // if dataUrl String
  4966. if (src.substr(0, 5) === 'data:') {
  4967. _load.call(this, new Blob(null, { data: src }), arguments[1]);
  4968. }
  4969. // else assume Url, either relative or absolute
  4970. else {
  4971. _loadFromUrl.apply(this, arguments);
  4972. }
  4973. }
  4974. // if source seems to be an img node
  4975. else if (srcType === 'node' && src.nodeName.toLowerCase() === 'img') {
  4976. _load.call(this, src.src, arguments[1]);
  4977. }
  4978. else {
  4979. throw new x.DOMException(x.DOMException.TYPE_MISMATCH_ERR);
  4980. }
  4981. } catch(ex) {
  4982. // for now simply trigger error event
  4983. this.trigger('error', ex.code);
  4984. }
  4985. }
  4986. function _loadFromImage(img, exact) {
  4987. var runtime = this.connectRuntime(img.ruid);
  4988. this.ruid = runtime.uid;
  4989. runtime.exec.call(this, 'Image', 'loadFromImage', img, (Basic.typeOf(exact) === 'undefined' ? true : exact));
  4990. }
  4991. function _loadFromBlob(blob, options) {
  4992. var self = this;
  4993. self.name = blob.name || '';
  4994. function exec(runtime) {
  4995. self.ruid = runtime.uid;
  4996. runtime.exec.call(self, 'Image', 'loadFromBlob', blob);
  4997. }
  4998. if (blob.isDetached()) {
  4999. this.bind('RuntimeInit', function(e, runtime) {
  5000. exec(runtime);
  5001. });
  5002. // convert to object representation
  5003. if (options && typeof(options.required_caps) === 'string') {
  5004. options.required_caps = Runtime.parseCaps(options.required_caps);
  5005. }
  5006. this.connectRuntime(Basic.extend({
  5007. required_caps: {
  5008. access_image_binary: true,
  5009. resize_image: true
  5010. }
  5011. }, options));
  5012. } else {
  5013. exec(this.connectRuntime(blob.ruid));
  5014. }
  5015. }
  5016. function _loadFromUrl(url, options) {
  5017. var self = this, xhr;
  5018. xhr = new XMLHttpRequest();
  5019. xhr.open('get', url);
  5020. xhr.responseType = 'blob';
  5021. xhr.onprogress = function(e) {
  5022. self.trigger(e);
  5023. };
  5024. xhr.onload = function() {
  5025. _loadFromBlob.call(self, xhr.response, true);
  5026. };
  5027. xhr.onerror = function(e) {
  5028. self.trigger(e);
  5029. };
  5030. xhr.onloadend = function() {
  5031. xhr.destroy();
  5032. };
  5033. xhr.bind('RuntimeError', function(e, err) {
  5034. self.trigger('RuntimeError', err);
  5035. });
  5036. xhr.send(null, options);
  5037. }
  5038. }
  5039. // virtual world will crash on you if image has a resolution higher than this:
  5040. Image.MAX_RESIZE_WIDTH = 8192;
  5041. Image.MAX_RESIZE_HEIGHT = 8192;
  5042. Image.prototype = EventTarget.instance;
  5043. return Image;
  5044. });
  5045. // Included from: src/javascript/runtime/html5/Runtime.js
  5046. /**
  5047. * Runtime.js
  5048. *
  5049. * Copyright 2013, Moxiecode Systems AB
  5050. * Released under GPL License.
  5051. *
  5052. * License: http://www.plupload.com/license
  5053. * Contributing: http://www.plupload.com/contributing
  5054. */
  5055. /*global File:true */
  5056. /**
  5057. Defines constructor for HTML5 runtime.
  5058. @class moxie/runtime/html5/Runtime
  5059. @private
  5060. */
  5061. define("moxie/runtime/html5/Runtime", [
  5062. "moxie/core/utils/Basic",
  5063. "moxie/core/Exceptions",
  5064. "moxie/runtime/Runtime",
  5065. "moxie/core/utils/Env"
  5066. ], function(Basic, x, Runtime, Env) {
  5067. var type = "html5", extensions = {};
  5068. function Html5Runtime(options) {
  5069. var I = this
  5070. , Test = Runtime.capTest
  5071. , True = Runtime.capTrue
  5072. ;
  5073. var caps = Basic.extend({
  5074. access_binary: Test(window.FileReader || window.File && window.File.getAsDataURL),
  5075. access_image_binary: function() {
  5076. return I.can('access_binary') && !!extensions.Image;
  5077. },
  5078. display_media: Test(Env.can('create_canvas') || Env.can('use_data_uri_over32kb')),
  5079. do_cors: Test(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()),
  5080. drag_and_drop: Test(function() {
  5081. // this comes directly from Modernizr: http://www.modernizr.com/
  5082. var div = document.createElement('div');
  5083. // IE has support for drag and drop since version 5, but doesn't support dropping files from desktop
  5084. return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) &&
  5085. (Env.browser !== 'IE' || Env.verComp(Env.version, 9, '>'));
  5086. }()),
  5087. filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
  5088. return (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '>=')) ||
  5089. (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
  5090. (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '>='));
  5091. }()),
  5092. return_response_headers: True,
  5093. return_response_type: function(responseType) {
  5094. if (responseType === 'json' && !!window.JSON) { // we can fake this one even if it's not supported
  5095. return true;
  5096. }
  5097. return Env.can('return_response_type', responseType);
  5098. },
  5099. return_status_code: True,
  5100. report_upload_progress: Test(window.XMLHttpRequest && new XMLHttpRequest().upload),
  5101. resize_image: function() {
  5102. return I.can('access_binary') && Env.can('create_canvas');
  5103. },
  5104. select_file: function() {
  5105. return Env.can('use_fileinput') && window.File;
  5106. },
  5107. select_folder: function() {
  5108. return I.can('select_file') && Env.browser === 'Chrome' && Env.verComp(Env.version, 21, '>=');
  5109. },
  5110. select_multiple: function() {
  5111. // it is buggy on Safari Windows and iOS
  5112. return I.can('select_file') &&
  5113. !(Env.browser === 'Safari' && Env.os === 'Windows') &&
  5114. !(Env.os === 'iOS' && Env.verComp(Env.osVersion, "7.0.0", '>') && Env.verComp(Env.osVersion, "8.0.0", '<'));
  5115. },
  5116. send_binary_string: Test(window.XMLHttpRequest && (new XMLHttpRequest().sendAsBinary || (window.Uint8Array && window.ArrayBuffer))),
  5117. send_custom_headers: Test(window.XMLHttpRequest),
  5118. send_multipart: function() {
  5119. return !!(window.XMLHttpRequest && new XMLHttpRequest().upload && window.FormData) || I.can('send_binary_string');
  5120. },
  5121. slice_blob: Test(window.File && (File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice)),
  5122. stream_upload: function(){
  5123. return I.can('slice_blob') && I.can('send_multipart');
  5124. },
  5125. summon_file_dialog: function() { // yeah... some dirty sniffing here...
  5126. return I.can('select_file') && (
  5127. (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '>=')) ||
  5128. (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '>=')) ||
  5129. (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
  5130. !!~Basic.inArray(Env.browser, ['Chrome', 'Safari'])
  5131. );
  5132. },
  5133. upload_filesize: True
  5134. },
  5135. arguments[2]
  5136. );
  5137. Runtime.call(this, options, (arguments[1] || type), caps);
  5138. Basic.extend(this, {
  5139. init : function() {
  5140. this.trigger("Init");
  5141. },
  5142. destroy: (function(destroy) { // extend default destroy method
  5143. return function() {
  5144. destroy.call(I);
  5145. destroy = I = null;
  5146. };
  5147. }(this.destroy))
  5148. });
  5149. Basic.extend(this.getShim(), extensions);
  5150. }
  5151. Runtime.addConstructor(type, Html5Runtime);
  5152. return extensions;
  5153. });
  5154. // Included from: src/javascript/core/utils/Events.js
  5155. /**
  5156. * Events.js
  5157. *
  5158. * Copyright 2013, Moxiecode Systems AB
  5159. * Released under GPL License.
  5160. *
  5161. * License: http://www.plupload.com/license
  5162. * Contributing: http://www.plupload.com/contributing
  5163. */
  5164. define('moxie/core/utils/Events', [
  5165. 'moxie/core/utils/Basic'
  5166. ], function(Basic) {
  5167. var eventhash = {}, uid = 'moxie_' + Basic.guid();
  5168. // IE W3C like event funcs
  5169. function preventDefault() {
  5170. this.returnValue = false;
  5171. }
  5172. function stopPropagation() {
  5173. this.cancelBubble = true;
  5174. }
  5175. /**
  5176. Adds an event handler to the specified object and store reference to the handler
  5177. in objects internal Plupload registry (@see removeEvent).
  5178. @method addEvent
  5179. @for Utils
  5180. @static
  5181. @param {Object} obj DOM element like object to add handler to.
  5182. @param {String} name Name to add event listener to.
  5183. @param {Function} callback Function to call when event occurs.
  5184. @param {String} [key] that might be used to add specifity to the event record.
  5185. */
  5186. var addEvent = function(obj, name, callback, key) {
  5187. var func, events;
  5188. name = name.toLowerCase();
  5189. // Add event listener
  5190. if (obj.addEventListener) {
  5191. func = callback;
  5192. obj.addEventListener(name, func, false);
  5193. } else if (obj.attachEvent) {
  5194. func = function() {
  5195. var evt = window.event;
  5196. if (!evt.target) {
  5197. evt.target = evt.srcElement;
  5198. }
  5199. evt.preventDefault = preventDefault;
  5200. evt.stopPropagation = stopPropagation;
  5201. callback(evt);
  5202. };
  5203. obj.attachEvent('on' + name, func);
  5204. }
  5205. // Log event handler to objects internal mOxie registry
  5206. if (!obj[uid]) {
  5207. obj[uid] = Basic.guid();
  5208. }
  5209. if (!eventhash.hasOwnProperty(obj[uid])) {
  5210. eventhash[obj[uid]] = {};
  5211. }
  5212. events = eventhash[obj[uid]];
  5213. if (!events.hasOwnProperty(name)) {
  5214. events[name] = [];
  5215. }
  5216. events[name].push({
  5217. func: func,
  5218. orig: callback, // store original callback for IE
  5219. key: key
  5220. });
  5221. };
  5222. /**
  5223. Remove event handler from the specified object. If third argument (callback)
  5224. is not specified remove all events with the specified name.
  5225. @method removeEvent
  5226. @static
  5227. @param {Object} obj DOM element to remove event listener(s) from.
  5228. @param {String} name Name of event listener to remove.
  5229. @param {Function|String} [callback] might be a callback or unique key to match.
  5230. */
  5231. var removeEvent = function(obj, name, callback) {
  5232. var type, undef;
  5233. name = name.toLowerCase();
  5234. if (obj[uid] && eventhash[obj[uid]] && eventhash[obj[uid]][name]) {
  5235. type = eventhash[obj[uid]][name];
  5236. } else {
  5237. return;
  5238. }
  5239. for (var i = type.length - 1; i >= 0; i--) {
  5240. // undefined or not, key should match
  5241. if (type[i].orig === callback || type[i].key === callback) {
  5242. if (obj.removeEventListener) {
  5243. obj.removeEventListener(name, type[i].func, false);
  5244. } else if (obj.detachEvent) {
  5245. obj.detachEvent('on'+name, type[i].func);
  5246. }
  5247. type[i].orig = null;
  5248. type[i].func = null;
  5249. type.splice(i, 1);
  5250. // If callback was passed we are done here, otherwise proceed
  5251. if (callback !== undef) {
  5252. break;
  5253. }
  5254. }
  5255. }
  5256. // If event array got empty, remove it
  5257. if (!type.length) {
  5258. delete eventhash[obj[uid]][name];
  5259. }
  5260. // If mOxie registry has become empty, remove it
  5261. if (Basic.isEmptyObj(eventhash[obj[uid]])) {
  5262. delete eventhash[obj[uid]];
  5263. // IE doesn't let you remove DOM object property with - delete
  5264. try {
  5265. delete obj[uid];
  5266. } catch(e) {
  5267. obj[uid] = undef;
  5268. }
  5269. }
  5270. };
  5271. /**
  5272. Remove all kind of events from the specified object
  5273. @method removeAllEvents
  5274. @static
  5275. @param {Object} obj DOM element to remove event listeners from.
  5276. @param {String} [key] unique key to match, when removing events.
  5277. */
  5278. var removeAllEvents = function(obj, key) {
  5279. if (!obj || !obj[uid]) {
  5280. return;
  5281. }
  5282. Basic.each(eventhash[obj[uid]], function(events, name) {
  5283. removeEvent(obj, name, key);
  5284. });
  5285. };
  5286. return {
  5287. addEvent: addEvent,
  5288. removeEvent: removeEvent,
  5289. removeAllEvents: removeAllEvents
  5290. };
  5291. });
  5292. // Included from: src/javascript/runtime/html5/file/FileInput.js
  5293. /**
  5294. * FileInput.js
  5295. *
  5296. * Copyright 2013, Moxiecode Systems AB
  5297. * Released under GPL License.
  5298. *
  5299. * License: http://www.plupload.com/license
  5300. * Contributing: http://www.plupload.com/contributing
  5301. */
  5302. /**
  5303. @class moxie/runtime/html5/file/FileInput
  5304. @private
  5305. */
  5306. define("moxie/runtime/html5/file/FileInput", [
  5307. "moxie/runtime/html5/Runtime",
  5308. "moxie/file/File",
  5309. "moxie/core/utils/Basic",
  5310. "moxie/core/utils/Dom",
  5311. "moxie/core/utils/Events",
  5312. "moxie/core/utils/Mime",
  5313. "moxie/core/utils/Env"
  5314. ], function(extensions, File, Basic, Dom, Events, Mime, Env) {
  5315. function FileInput() {
  5316. var _options;
  5317. Basic.extend(this, {
  5318. init: function(options) {
  5319. var comp = this, I = comp.getRuntime(), input, shimContainer, mimes, browseButton, zIndex, top;
  5320. _options = options;
  5321. // figure out accept string
  5322. mimes = _options.accept.mimes || Mime.extList2mimes(_options.accept, I.can('filter_by_extension'));
  5323. shimContainer = I.getShimContainer();
  5324. shimContainer.innerHTML = '<input id="' + I.uid +'" type="file" style="font-size:999px;opacity:0;"' +
  5325. (_options.multiple && I.can('select_multiple') ? 'multiple' : '') +
  5326. (_options.directory && I.can('select_folder') ? 'webkitdirectory directory' : '') + // Chrome 11+
  5327. (mimes ? ' accept="' + mimes.join(',') + '"' : '') + ' />';
  5328. input = Dom.get(I.uid);
  5329. // prepare file input to be placed underneath the browse_button element
  5330. Basic.extend(input.style, {
  5331. position: 'absolute',
  5332. top: 0,
  5333. left: 0,
  5334. width: '100%',
  5335. height: '100%'
  5336. });
  5337. browseButton = Dom.get(_options.browse_button);
  5338. // Route click event to the input[type=file] element for browsers that support such behavior
  5339. if (I.can('summon_file_dialog')) {
  5340. if (Dom.getStyle(browseButton, 'position') === 'static') {
  5341. browseButton.style.position = 'relative';
  5342. }
  5343. zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
  5344. browseButton.style.zIndex = zIndex;
  5345. shimContainer.style.zIndex = zIndex - 1;
  5346. Events.addEvent(browseButton, 'click', function(e) {
  5347. var input = Dom.get(I.uid);
  5348. if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
  5349. input.click();
  5350. }
  5351. e.preventDefault();
  5352. }, comp.uid);
  5353. }
  5354. /* Since we have to place input[type=file] on top of the browse_button for some browsers,
  5355. browse_button loses interactivity, so we restore it here */
  5356. top = I.can('summon_file_dialog') ? browseButton : shimContainer;
  5357. Events.addEvent(top, 'mouseover', function() {
  5358. comp.trigger('mouseenter');
  5359. }, comp.uid);
  5360. Events.addEvent(top, 'mouseout', function() {
  5361. comp.trigger('mouseleave');
  5362. }, comp.uid);
  5363. Events.addEvent(top, 'mousedown', function() {
  5364. comp.trigger('mousedown');
  5365. }, comp.uid);
  5366. Events.addEvent(Dom.get(_options.container), 'mouseup', function() {
  5367. comp.trigger('mouseup');
  5368. }, comp.uid);
  5369. input.onchange = function onChange(e) { // there should be only one handler for this
  5370. comp.files = [];
  5371. Basic.each(this.files, function(file) {
  5372. var relativePath = '';
  5373. if (_options.directory) {
  5374. // folders are represented by dots, filter them out (Chrome 11+)
  5375. if (file.name == ".") {
  5376. // if it looks like a folder...
  5377. return true;
  5378. }
  5379. }
  5380. if (file.webkitRelativePath) {
  5381. relativePath = '/' + file.webkitRelativePath.replace(/^\//, '');
  5382. }
  5383. file = new File(I.uid, file);
  5384. file.relativePath = relativePath;
  5385. comp.files.push(file);
  5386. });
  5387. // clearing the value enables the user to select the same file again if they want to
  5388. if (Env.browser !== 'IE' && Env.browser !== 'IEMobile') {
  5389. this.value = '';
  5390. } else {
  5391. // in IE input[type="file"] is read-only so the only way to reset it is to re-insert it
  5392. var clone = this.cloneNode(true);
  5393. this.parentNode.replaceChild(clone, this);
  5394. clone.onchange = onChange;
  5395. }
  5396. if (comp.files.length) {
  5397. comp.trigger('change');
  5398. }
  5399. };
  5400. // ready event is perfectly asynchronous
  5401. comp.trigger({
  5402. type: 'ready',
  5403. async: true
  5404. });
  5405. shimContainer = null;
  5406. },
  5407. disable: function(state) {
  5408. var I = this.getRuntime(), input;
  5409. if ((input = Dom.get(I.uid))) {
  5410. input.disabled = !!state;
  5411. }
  5412. },
  5413. destroy: function() {
  5414. var I = this.getRuntime()
  5415. , shim = I.getShim()
  5416. , shimContainer = I.getShimContainer()
  5417. ;
  5418. Events.removeAllEvents(shimContainer, this.uid);
  5419. Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
  5420. Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
  5421. if (shimContainer) {
  5422. shimContainer.innerHTML = '';
  5423. }
  5424. shim.removeInstance(this.uid);
  5425. _options = shimContainer = shim = null;
  5426. }
  5427. });
  5428. }
  5429. return (extensions.FileInput = FileInput);
  5430. });
  5431. // Included from: src/javascript/runtime/html5/file/Blob.js
  5432. /**
  5433. * Blob.js
  5434. *
  5435. * Copyright 2013, Moxiecode Systems AB
  5436. * Released under GPL License.
  5437. *
  5438. * License: http://www.plupload.com/license
  5439. * Contributing: http://www.plupload.com/contributing
  5440. */
  5441. /**
  5442. @class moxie/runtime/html5/file/Blob
  5443. @private
  5444. */
  5445. define("moxie/runtime/html5/file/Blob", [
  5446. "moxie/runtime/html5/Runtime",
  5447. "moxie/file/Blob"
  5448. ], function(extensions, Blob) {
  5449. function HTML5Blob() {
  5450. function w3cBlobSlice(blob, start, end) {
  5451. var blobSlice;
  5452. if (window.File.prototype.slice) {
  5453. try {
  5454. blob.slice(); // depricated version will throw WRONG_ARGUMENTS_ERR exception
  5455. return blob.slice(start, end);
  5456. } catch (e) {
  5457. // depricated slice method
  5458. return blob.slice(start, end - start);
  5459. }
  5460. // slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672
  5461. } else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) {
  5462. return blobSlice.call(blob, start, end);
  5463. } else {
  5464. return null; // or throw some exception
  5465. }
  5466. }
  5467. this.slice = function() {
  5468. return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments));
  5469. };
  5470. }
  5471. return (extensions.Blob = HTML5Blob);
  5472. });
  5473. // Included from: src/javascript/runtime/html5/file/FileDrop.js
  5474. /**
  5475. * FileDrop.js
  5476. *
  5477. * Copyright 2013, Moxiecode Systems AB
  5478. * Released under GPL License.
  5479. *
  5480. * License: http://www.plupload.com/license
  5481. * Contributing: http://www.plupload.com/contributing
  5482. */
  5483. /**
  5484. @class moxie/runtime/html5/file/FileDrop
  5485. @private
  5486. */
  5487. define("moxie/runtime/html5/file/FileDrop", [
  5488. "moxie/runtime/html5/Runtime",
  5489. 'moxie/file/File',
  5490. "moxie/core/utils/Basic",
  5491. "moxie/core/utils/Dom",
  5492. "moxie/core/utils/Events",
  5493. "moxie/core/utils/Mime"
  5494. ], function(extensions, File, Basic, Dom, Events, Mime) {
  5495. function FileDrop() {
  5496. var _files = [], _allowedExts = [], _options, _ruid;
  5497. Basic.extend(this, {
  5498. init: function(options) {
  5499. var comp = this, dropZone;
  5500. _options = options;
  5501. _ruid = comp.ruid; // every dropped-in file should have a reference to the runtime
  5502. _allowedExts = _extractExts(_options.accept);
  5503. dropZone = _options.container;
  5504. Events.addEvent(dropZone, 'dragover', function(e) {
  5505. if (!_hasFiles(e)) {
  5506. return;
  5507. }
  5508. e.preventDefault();
  5509. e.dataTransfer.dropEffect = 'copy';
  5510. }, comp.uid);
  5511. Events.addEvent(dropZone, 'drop', function(e) {
  5512. if (!_hasFiles(e)) {
  5513. return;
  5514. }
  5515. e.preventDefault();
  5516. _files = [];
  5517. // Chrome 21+ accepts folders via Drag'n'Drop
  5518. if (e.dataTransfer.items && e.dataTransfer.items[0].webkitGetAsEntry) {
  5519. _readItems(e.dataTransfer.items, function() {
  5520. comp.files = _files;
  5521. comp.trigger("drop");
  5522. });
  5523. } else {
  5524. Basic.each(e.dataTransfer.files, function(file) {
  5525. _addFile(file);
  5526. });
  5527. comp.files = _files;
  5528. comp.trigger("drop");
  5529. }
  5530. }, comp.uid);
  5531. Events.addEvent(dropZone, 'dragenter', function(e) {
  5532. comp.trigger("dragenter");
  5533. }, comp.uid);
  5534. Events.addEvent(dropZone, 'dragleave', function(e) {
  5535. comp.trigger("dragleave");
  5536. }, comp.uid);
  5537. },
  5538. destroy: function() {
  5539. Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
  5540. _ruid = _files = _allowedExts = _options = null;
  5541. }
  5542. });
  5543. function _hasFiles(e) {
  5544. if (!e.dataTransfer || !e.dataTransfer.types) { // e.dataTransfer.files is not available in Gecko during dragover
  5545. return false;
  5546. }
  5547. var types = Basic.toArray(e.dataTransfer.types || []);
  5548. return Basic.inArray("Files", types) !== -1 ||
  5549. Basic.inArray("public.file-url", types) !== -1 || // Safari < 5
  5550. Basic.inArray("application/x-moz-file", types) !== -1 // Gecko < 1.9.2 (< Firefox 3.6)
  5551. ;
  5552. }
  5553. function _addFile(file, relativePath) {
  5554. if (_isAcceptable(file)) {
  5555. var fileObj = new File(_ruid, file);
  5556. fileObj.relativePath = relativePath || '';
  5557. _files.push(fileObj);
  5558. }
  5559. }
  5560. function _extractExts(accept) {
  5561. var exts = [];
  5562. for (var i = 0; i < accept.length; i++) {
  5563. [].push.apply(exts, accept[i].extensions.split(/\s*,\s*/));
  5564. }
  5565. return Basic.inArray('*', exts) === -1 ? exts : [];
  5566. }
  5567. function _isAcceptable(file) {
  5568. if (!_allowedExts.length) {
  5569. return true;
  5570. }
  5571. var ext = Mime.getFileExtension(file.name);
  5572. return !ext || Basic.inArray(ext, _allowedExts) !== -1;
  5573. }
  5574. function _readItems(items, cb) {
  5575. var entries = [];
  5576. Basic.each(items, function(item) {
  5577. var entry = item.webkitGetAsEntry();
  5578. // Address #998 (https://code.google.com/p/chromium/issues/detail?id=332579)
  5579. if (entry) {
  5580. // file() fails on OSX when the filename contains a special character (e.g. umlaut): see #61
  5581. if (entry.isFile) {
  5582. _addFile(item.getAsFile(), entry.fullPath);
  5583. } else {
  5584. entries.push(entry);
  5585. }
  5586. }
  5587. });
  5588. if (entries.length) {
  5589. _readEntries(entries, cb);
  5590. } else {
  5591. cb();
  5592. }
  5593. }
  5594. function _readEntries(entries, cb) {
  5595. var queue = [];
  5596. Basic.each(entries, function(entry) {
  5597. queue.push(function(cbcb) {
  5598. _readEntry(entry, cbcb);
  5599. });
  5600. });
  5601. Basic.inSeries(queue, function() {
  5602. cb();
  5603. });
  5604. }
  5605. function _readEntry(entry, cb) {
  5606. if (entry.isFile) {
  5607. entry.file(function(file) {
  5608. _addFile(file, entry.fullPath);
  5609. cb();
  5610. }, function() {
  5611. // fire an error event maybe
  5612. cb();
  5613. });
  5614. } else if (entry.isDirectory) {
  5615. _readDirEntry(entry, cb);
  5616. } else {
  5617. cb(); // not file, not directory? what then?..
  5618. }
  5619. }
  5620. function _readDirEntry(dirEntry, cb) {
  5621. var entries = [], dirReader = dirEntry.createReader();
  5622. // keep quering recursively till no more entries
  5623. function getEntries(cbcb) {
  5624. dirReader.readEntries(function(moreEntries) {
  5625. if (moreEntries.length) {
  5626. [].push.apply(entries, moreEntries);
  5627. getEntries(cbcb);
  5628. } else {
  5629. cbcb();
  5630. }
  5631. }, cbcb);
  5632. }
  5633. // ...and you thought FileReader was crazy...
  5634. getEntries(function() {
  5635. _readEntries(entries, cb);
  5636. });
  5637. }
  5638. }
  5639. return (extensions.FileDrop = FileDrop);
  5640. });
  5641. // Included from: src/javascript/runtime/html5/file/FileReader.js
  5642. /**
  5643. * FileReader.js
  5644. *
  5645. * Copyright 2013, Moxiecode Systems AB
  5646. * Released under GPL License.
  5647. *
  5648. * License: http://www.plupload.com/license
  5649. * Contributing: http://www.plupload.com/contributing
  5650. */
  5651. /**
  5652. @class moxie/runtime/html5/file/FileReader
  5653. @private
  5654. */
  5655. define("moxie/runtime/html5/file/FileReader", [
  5656. "moxie/runtime/html5/Runtime",
  5657. "moxie/core/utils/Encode",
  5658. "moxie/core/utils/Basic"
  5659. ], function(extensions, Encode, Basic) {
  5660. function FileReader() {
  5661. var _fr, _convertToBinary = false;
  5662. Basic.extend(this, {
  5663. read: function(op, blob) {
  5664. var comp = this;
  5665. comp.result = '';
  5666. _fr = new window.FileReader();
  5667. _fr.addEventListener('progress', function(e) {
  5668. comp.trigger(e);
  5669. });
  5670. _fr.addEventListener('load', function(e) {
  5671. comp.result = _convertToBinary ? _toBinary(_fr.result) : _fr.result;
  5672. comp.trigger(e);
  5673. });
  5674. _fr.addEventListener('error', function(e) {
  5675. comp.trigger(e, _fr.error);
  5676. });
  5677. _fr.addEventListener('loadend', function(e) {
  5678. _fr = null;
  5679. comp.trigger(e);
  5680. });
  5681. if (Basic.typeOf(_fr[op]) === 'function') {
  5682. _convertToBinary = false;
  5683. _fr[op](blob.getSource());
  5684. } else if (op === 'readAsBinaryString') { // readAsBinaryString is depricated in general and never existed in IE10+
  5685. _convertToBinary = true;
  5686. _fr.readAsDataURL(blob.getSource());
  5687. }
  5688. },
  5689. abort: function() {
  5690. if (_fr) {
  5691. _fr.abort();
  5692. }
  5693. },
  5694. destroy: function() {
  5695. _fr = null;
  5696. }
  5697. });
  5698. function _toBinary(str) {
  5699. return Encode.atob(str.substring(str.indexOf('base64,') + 7));
  5700. }
  5701. }
  5702. return (extensions.FileReader = FileReader);
  5703. });
  5704. // Included from: src/javascript/runtime/html5/xhr/XMLHttpRequest.js
  5705. /**
  5706. * XMLHttpRequest.js
  5707. *
  5708. * Copyright 2013, Moxiecode Systems AB
  5709. * Released under GPL License.
  5710. *
  5711. * License: http://www.plupload.com/license
  5712. * Contributing: http://www.plupload.com/contributing
  5713. */
  5714. /*global ActiveXObject:true */
  5715. /**
  5716. @class moxie/runtime/html5/xhr/XMLHttpRequest
  5717. @private
  5718. */
  5719. define("moxie/runtime/html5/xhr/XMLHttpRequest", [
  5720. "moxie/runtime/html5/Runtime",
  5721. "moxie/core/utils/Basic",
  5722. "moxie/core/utils/Mime",
  5723. "moxie/core/utils/Url",
  5724. "moxie/file/File",
  5725. "moxie/file/Blob",
  5726. "moxie/xhr/FormData",
  5727. "moxie/core/Exceptions",
  5728. "moxie/core/utils/Env"
  5729. ], function(extensions, Basic, Mime, Url, File, Blob, FormData, x, Env) {
  5730. function XMLHttpRequest() {
  5731. var self = this
  5732. , _xhr
  5733. , _filename
  5734. ;
  5735. Basic.extend(this, {
  5736. send: function(meta, data) {
  5737. var target = this
  5738. , isGecko2_5_6 = (Env.browser === 'Mozilla' && Env.verComp(Env.version, 4, '>=') && Env.verComp(Env.version, 7, '<'))
  5739. , isAndroidBrowser = Env.browser === 'Android Browser'
  5740. , mustSendAsBinary = false
  5741. ;
  5742. // extract file name
  5743. _filename = meta.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase();
  5744. _xhr = _getNativeXHR();
  5745. _xhr.open(meta.method, meta.url, meta.async, meta.user, meta.password);
  5746. // prepare data to be sent
  5747. if (data instanceof Blob) {
  5748. if (data.isDetached()) {
  5749. mustSendAsBinary = true;
  5750. }
  5751. data = data.getSource();
  5752. } else if (data instanceof FormData) {
  5753. if (data.hasBlob()) {
  5754. if (data.getBlob().isDetached()) {
  5755. data = _prepareMultipart.call(target, data); // _xhr must be instantiated and be in OPENED state
  5756. mustSendAsBinary = true;
  5757. } else if ((isGecko2_5_6 || isAndroidBrowser) && Basic.typeOf(data.getBlob().getSource()) === 'blob' && window.FileReader) {
  5758. // Gecko 2/5/6 can't send blob in FormData: https://bugzilla.mozilla.org/show_bug.cgi?id=649150
  5759. // Android browsers (default one and Dolphin) seem to have the same issue, see: #613
  5760. _preloadAndSend.call(target, meta, data);
  5761. return; // _preloadAndSend will reinvoke send() with transmutated FormData =%D
  5762. }
  5763. }
  5764. // transfer fields to real FormData
  5765. if (data instanceof FormData) { // if still a FormData, e.g. not mangled by _prepareMultipart()
  5766. var fd = new window.FormData();
  5767. data.each(function(value, name) {
  5768. if (value instanceof Blob) {
  5769. fd.append(name, value.getSource());
  5770. } else {
  5771. fd.append(name, value);
  5772. }
  5773. });
  5774. data = fd;
  5775. }
  5776. }
  5777. // if XHR L2
  5778. if (_xhr.upload) {
  5779. if (meta.withCredentials) {
  5780. _xhr.withCredentials = true;
  5781. }
  5782. _xhr.addEventListener('load', function(e) {
  5783. target.trigger(e);
  5784. });
  5785. _xhr.addEventListener('error', function(e) {
  5786. target.trigger(e);
  5787. });
  5788. // additionally listen to progress events
  5789. _xhr.addEventListener('progress', function(e) {
  5790. target.trigger(e);
  5791. });
  5792. _xhr.upload.addEventListener('progress', function(e) {
  5793. target.trigger({
  5794. type: 'UploadProgress',
  5795. loaded: e.loaded,
  5796. total: e.total
  5797. });
  5798. });
  5799. // ... otherwise simulate XHR L2
  5800. } else {
  5801. _xhr.onreadystatechange = function onReadyStateChange() {
  5802. // fake Level 2 events
  5803. switch (_xhr.readyState) {
  5804. case 1: // XMLHttpRequest.OPENED
  5805. // readystatechanged is fired twice for OPENED state (in IE and Mozilla) - neu
  5806. break;
  5807. // looks like HEADERS_RECEIVED (state 2) is not reported in Opera (or it's old versions) - neu
  5808. case 2: // XMLHttpRequest.HEADERS_RECEIVED
  5809. break;
  5810. case 3: // XMLHttpRequest.LOADING
  5811. // try to fire progress event for not XHR L2
  5812. var total, loaded;
  5813. try {
  5814. if (Url.hasSameOrigin(meta.url)) { // Content-Length not accessible for cross-domain on some browsers
  5815. total = _xhr.getResponseHeader('Content-Length') || 0; // old Safari throws an exception here
  5816. }
  5817. if (_xhr.responseText) { // responseText was introduced in IE7
  5818. loaded = _xhr.responseText.length;
  5819. }
  5820. } catch(ex) {
  5821. total = loaded = 0;
  5822. }
  5823. target.trigger({
  5824. type: 'progress',
  5825. lengthComputable: !!total,
  5826. total: parseInt(total, 10),
  5827. loaded: loaded
  5828. });
  5829. break;
  5830. case 4: // XMLHttpRequest.DONE
  5831. // release readystatechange handler (mostly for IE)
  5832. _xhr.onreadystatechange = function() {};
  5833. // usually status 0 is returned when server is unreachable, but FF also fails to status 0 for 408 timeout
  5834. if (_xhr.status === 0) {
  5835. target.trigger('error');
  5836. } else {
  5837. target.trigger('load');
  5838. }
  5839. break;
  5840. }
  5841. };
  5842. }
  5843. // set request headers
  5844. if (!Basic.isEmptyObj(meta.headers)) {
  5845. Basic.each(meta.headers, function(value, header) {
  5846. _xhr.setRequestHeader(header, value);
  5847. });
  5848. }
  5849. // request response type
  5850. if ("" !== meta.responseType && 'responseType' in _xhr) {
  5851. if ('json' === meta.responseType && !Env.can('return_response_type', 'json')) { // we can fake this one
  5852. _xhr.responseType = 'text';
  5853. } else {
  5854. _xhr.responseType = meta.responseType;
  5855. }
  5856. }
  5857. // send ...
  5858. if (!mustSendAsBinary) {
  5859. _xhr.send(data);
  5860. } else {
  5861. if (_xhr.sendAsBinary) { // Gecko
  5862. _xhr.sendAsBinary(data);
  5863. } else { // other browsers having support for typed arrays
  5864. (function() {
  5865. // mimic Gecko's sendAsBinary
  5866. var ui8a = new Uint8Array(data.length);
  5867. for (var i = 0; i < data.length; i++) {
  5868. ui8a[i] = (data.charCodeAt(i) & 0xff);
  5869. }
  5870. _xhr.send(ui8a.buffer);
  5871. }());
  5872. }
  5873. }
  5874. target.trigger('loadstart');
  5875. },
  5876. getStatus: function() {
  5877. // according to W3C spec it should return 0 for readyState < 3, but instead it throws an exception
  5878. try {
  5879. if (_xhr) {
  5880. return _xhr.status;
  5881. }
  5882. } catch(ex) {}
  5883. return 0;
  5884. },
  5885. getResponse: function(responseType) {
  5886. var I = this.getRuntime();
  5887. try {
  5888. switch (responseType) {
  5889. case 'blob':
  5890. var file = new File(I.uid, _xhr.response);
  5891. // try to extract file name from content-disposition if possible (might be - not, if CORS for example)
  5892. var disposition = _xhr.getResponseHeader('Content-Disposition');
  5893. if (disposition) {
  5894. // extract filename from response header if available
  5895. var match = disposition.match(/filename=([\'\"'])([^\1]+)\1/);
  5896. if (match) {
  5897. _filename = match[2];
  5898. }
  5899. }
  5900. file.name = _filename;
  5901. // pre-webkit Opera doesn't set type property on the blob response
  5902. if (!file.type) {
  5903. file.type = Mime.getFileMime(_filename);
  5904. }
  5905. return file;
  5906. case 'json':
  5907. if (!Env.can('return_response_type', 'json')) {
  5908. return _xhr.status === 200 && !!window.JSON ? JSON.parse(_xhr.responseText) : null;
  5909. }
  5910. return _xhr.response;
  5911. case 'document':
  5912. return _getDocument(_xhr);
  5913. default:
  5914. return _xhr.responseText !== '' ? _xhr.responseText : null; // against the specs, but for consistency across the runtimes
  5915. }
  5916. } catch(ex) {
  5917. return null;
  5918. }
  5919. },
  5920. getAllResponseHeaders: function() {
  5921. try {
  5922. return _xhr.getAllResponseHeaders();
  5923. } catch(ex) {}
  5924. return '';
  5925. },
  5926. abort: function() {
  5927. if (_xhr) {
  5928. _xhr.abort();
  5929. }
  5930. },
  5931. destroy: function() {
  5932. self = _filename = null;
  5933. }
  5934. });
  5935. // here we go... ugly fix for ugly bug
  5936. function _preloadAndSend(meta, data) {
  5937. var target = this, blob, fr;
  5938. // get original blob
  5939. blob = data.getBlob().getSource();
  5940. // preload blob in memory to be sent as binary string
  5941. fr = new window.FileReader();
  5942. fr.onload = function() {
  5943. // overwrite original blob
  5944. data.append(data.getBlobName(), new Blob(null, {
  5945. type: blob.type,
  5946. data: fr.result
  5947. }));
  5948. // invoke send operation again
  5949. self.send.call(target, meta, data);
  5950. };
  5951. fr.readAsBinaryString(blob);
  5952. }
  5953. function _getNativeXHR() {
  5954. if (window.XMLHttpRequest && !(Env.browser === 'IE' && Env.verComp(Env.version, 8, '<'))) { // IE7 has native XHR but it's buggy
  5955. return new window.XMLHttpRequest();
  5956. } else {
  5957. return (function() {
  5958. var progIDs = ['Msxml2.XMLHTTP.6.0', 'Microsoft.XMLHTTP']; // if 6.0 available, use it, otherwise failback to default 3.0
  5959. for (var i = 0; i < progIDs.length; i++) {
  5960. try {
  5961. return new ActiveXObject(progIDs[i]);
  5962. } catch (ex) {}
  5963. }
  5964. })();
  5965. }
  5966. }
  5967. // @credits Sergey Ilinsky (http://www.ilinsky.com/)
  5968. function _getDocument(xhr) {
  5969. var rXML = xhr.responseXML;
  5970. var rText = xhr.responseText;
  5971. // Try parsing responseText (@see: http://www.ilinsky.com/articles/XMLHttpRequest/#bugs-ie-responseXML-content-type)
  5972. if (Env.browser === 'IE' && rText && rXML && !rXML.documentElement && /[^\/]+\/[^\+]+\+xml/.test(xhr.getResponseHeader("Content-Type"))) {
  5973. rXML = new window.ActiveXObject("Microsoft.XMLDOM");
  5974. rXML.async = false;
  5975. rXML.validateOnParse = false;
  5976. rXML.loadXML(rText);
  5977. }
  5978. // Check if there is no error in document
  5979. if (rXML) {
  5980. if ((Env.browser === 'IE' && rXML.parseError !== 0) || !rXML.documentElement || rXML.documentElement.tagName === "parsererror") {
  5981. return null;
  5982. }
  5983. }
  5984. return rXML;
  5985. }
  5986. function _prepareMultipart(fd) {
  5987. var boundary = '----moxieboundary' + new Date().getTime()
  5988. , dashdash = '--'
  5989. , crlf = '\r\n'
  5990. , multipart = ''
  5991. , I = this.getRuntime()
  5992. ;
  5993. if (!I.can('send_binary_string')) {
  5994. throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  5995. }
  5996. _xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
  5997. // append multipart parameters
  5998. fd.each(function(value, name) {
  5999. // Firefox 3.6 failed to convert multibyte characters to UTF-8 in sendAsBinary(),
  6000. // so we try it here ourselves with: unescape(encodeURIComponent(value))
  6001. if (value instanceof Blob) {
  6002. // Build RFC2388 blob
  6003. multipart += dashdash + boundary + crlf +
  6004. 'Content-Disposition: form-data; name="' + name + '"; filename="' + unescape(encodeURIComponent(value.name || 'blob')) + '"' + crlf +
  6005. 'Content-Type: ' + (value.type || 'application/octet-stream') + crlf + crlf +
  6006. value.getSource() + crlf;
  6007. } else {
  6008. multipart += dashdash + boundary + crlf +
  6009. 'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf +
  6010. unescape(encodeURIComponent(value)) + crlf;
  6011. }
  6012. });
  6013. multipart += dashdash + boundary + dashdash + crlf;
  6014. return multipart;
  6015. }
  6016. }
  6017. return (extensions.XMLHttpRequest = XMLHttpRequest);
  6018. });
  6019. // Included from: src/javascript/runtime/html5/utils/BinaryReader.js
  6020. /**
  6021. * BinaryReader.js
  6022. *
  6023. * Copyright 2013, Moxiecode Systems AB
  6024. * Released under GPL License.
  6025. *
  6026. * License: http://www.plupload.com/license
  6027. * Contributing: http://www.plupload.com/contributing
  6028. */
  6029. /**
  6030. @class moxie/runtime/html5/utils/BinaryReader
  6031. @private
  6032. */
  6033. define("moxie/runtime/html5/utils/BinaryReader", [
  6034. "moxie/core/utils/Basic"
  6035. ], function(Basic) {
  6036. function BinaryReader(data) {
  6037. if (data instanceof ArrayBuffer) {
  6038. ArrayBufferReader.apply(this, arguments);
  6039. } else {
  6040. UTF16StringReader.apply(this, arguments);
  6041. }
  6042. }
  6043.  
  6044. Basic.extend(BinaryReader.prototype, {
  6045. littleEndian: false,
  6046. read: function(idx, size) {
  6047. var sum, mv, i;
  6048. if (idx + size > this.length()) {
  6049. throw new Error("You are trying to read outside the source boundaries.");
  6050. }
  6051. mv = this.littleEndian
  6052. ? 0
  6053. : -8 * (size - 1)
  6054. ;
  6055. for (i = 0, sum = 0; i < size; i++) {
  6056. sum |= (this.readByteAt(idx + i) << Math.abs(mv + i*8));
  6057. }
  6058. return sum;
  6059. },
  6060. write: function(idx, num, size) {
  6061. var mv, i, str = '';
  6062. if (idx > this.length()) {
  6063. throw new Error("You are trying to write outside the source boundaries.");
  6064. }
  6065. mv = this.littleEndian
  6066. ? 0
  6067. : -8 * (size - 1)
  6068. ;
  6069. for (i = 0; i < size; i++) {
  6070. this.writeByteAt(idx + i, (num >> Math.abs(mv + i*8)) & 255);
  6071. }
  6072. },
  6073. BYTE: function(idx) {
  6074. return this.read(idx, 1);
  6075. },
  6076. SHORT: function(idx) {
  6077. return this.read(idx, 2);
  6078. },
  6079. LONG: function(idx) {
  6080. return this.read(idx, 4);
  6081. },
  6082. SLONG: function(idx) { // 2's complement notation
  6083. var num = this.read(idx, 4);
  6084. return (num > 2147483647 ? num - 4294967296 : num);
  6085. },
  6086. CHAR: function(idx) {
  6087. return String.fromCharCode(this.read(idx, 1));
  6088. },
  6089. STRING: function(idx, count) {
  6090. return this.asArray('CHAR', idx, count).join('');
  6091. },
  6092. asArray: function(type, idx, count) {
  6093. var values = [];
  6094. for (var i = 0; i < count; i++) {
  6095. values[i] = this[type](idx + i);
  6096. }
  6097. return values;
  6098. }
  6099. });
  6100. function ArrayBufferReader(data) {
  6101. var _dv = new DataView(data);
  6102. Basic.extend(this, {
  6103. readByteAt: function(idx) {
  6104. return _dv.getUint8(idx);
  6105. },
  6106. writeByteAt: function(idx, value) {
  6107. _dv.setUint8(idx, value);
  6108. },
  6109. SEGMENT: function(idx, size, value) {
  6110. switch (arguments.length) {
  6111. case 2:
  6112. return data.slice(idx, idx + size);
  6113. case 1:
  6114. return data.slice(idx);
  6115. case 3:
  6116. if (value === null) {
  6117. value = new ArrayBuffer();
  6118. }
  6119. if (value instanceof ArrayBuffer) {
  6120. var arr = new Uint8Array(this.length() - size + value.byteLength);
  6121. if (idx > 0) {
  6122. arr.set(new Uint8Array(data.slice(0, idx)), 0);
  6123. }
  6124. arr.set(new Uint8Array(value), idx);
  6125. arr.set(new Uint8Array(data.slice(idx + size)), idx + value.byteLength);
  6126. this.clear();
  6127. data = arr.buffer;
  6128. _dv = new DataView(data);
  6129. break;
  6130. }
  6131. default: return data;
  6132. }
  6133. },
  6134. length: function() {
  6135. return data ? data.byteLength : 0;
  6136. },
  6137. clear: function() {
  6138. _dv = data = null;
  6139. }
  6140. });
  6141. }
  6142. function UTF16StringReader(data) {
  6143. Basic.extend(this, {
  6144. readByteAt: function(idx) {
  6145. return data.charCodeAt(idx);
  6146. },
  6147. writeByteAt: function(idx, value) {
  6148. putstr(String.fromCharCode(value), idx, 1);
  6149. },
  6150. SEGMENT: function(idx, length, segment) {
  6151. switch (arguments.length) {
  6152. case 1:
  6153. return data.substr(idx);
  6154. case 2:
  6155. return data.substr(idx, length);
  6156. case 3:
  6157. putstr(segment !== null ? segment : '', idx, length);
  6158. break;
  6159. default: return data;
  6160. }
  6161. },
  6162. length: function() {
  6163. return data ? data.length : 0;
  6164. },
  6165. clear: function() {
  6166. data = null;
  6167. }
  6168. });
  6169. function putstr(segment, idx, length) {
  6170. length = arguments.length === 3 ? length : data.length - idx - 1;
  6171. data = data.substr(0, idx) + segment + data.substr(length + idx);
  6172. }
  6173. }
  6174. return BinaryReader;
  6175. });
  6176. // Included from: src/javascript/runtime/html5/image/JPEGHeaders.js
  6177. /**
  6178. * JPEGHeaders.js
  6179. *
  6180. * Copyright 2013, Moxiecode Systems AB
  6181. * Released under GPL License.
  6182. *
  6183. * License: http://www.plupload.com/license
  6184. * Contributing: http://www.plupload.com/contributing
  6185. */
  6186. /**
  6187. @class moxie/runtime/html5/image/JPEGHeaders
  6188. @private
  6189. */
  6190. define("moxie/runtime/html5/image/JPEGHeaders", [
  6191. "moxie/runtime/html5/utils/BinaryReader",
  6192. "moxie/core/Exceptions"
  6193. ], function(BinaryReader, x) {
  6194. return function JPEGHeaders(data) {
  6195. var headers = [], _br, idx, marker, length = 0;
  6196. _br = new BinaryReader(data);
  6197. // Check if data is jpeg
  6198. if (_br.SHORT(0) !== 0xFFD8) {
  6199. _br.clear();
  6200. throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  6201. }
  6202. idx = 2;
  6203. while (idx <= _br.length()) {
  6204. marker = _br.SHORT(idx);
  6205. // omit RST (restart) markers
  6206. if (marker >= 0xFFD0 && marker <= 0xFFD7) {
  6207. idx += 2;
  6208. continue;
  6209. }
  6210. // no headers allowed after SOS marker
  6211. if (marker === 0xFFDA || marker === 0xFFD9) {
  6212. break;
  6213. }
  6214. length = _br.SHORT(idx + 2) + 2;
  6215. // APPn marker detected
  6216. if (marker >= 0xFFE1 && marker <= 0xFFEF) {
  6217. headers.push({
  6218. hex: marker,
  6219. name: 'APP' + (marker & 0x000F),
  6220. start: idx,
  6221. length: length,
  6222. segment: _br.SEGMENT(idx, length)
  6223. });
  6224. }
  6225. idx += length;
  6226. }
  6227. _br.clear();
  6228. return {
  6229. headers: headers,
  6230. restore: function(data) {
  6231. var max, i, br;
  6232. br = new BinaryReader(data);
  6233. idx = br.SHORT(2) == 0xFFE0 ? 4 + br.SHORT(4) : 2;
  6234. for (i = 0, max = headers.length; i < max; i++) {
  6235. br.SEGMENT(idx, 0, headers[i].segment);
  6236. idx += headers[i].length;
  6237. }
  6238. data = br.SEGMENT();
  6239. br.clear();
  6240. return data;
  6241. },
  6242. strip: function(data) {
  6243. var br, headers, jpegHeaders, i;
  6244. jpegHeaders = new JPEGHeaders(data);
  6245. headers = jpegHeaders.headers;
  6246. jpegHeaders.purge();
  6247. br = new BinaryReader(data);
  6248. i = headers.length;
  6249. while (i--) {
  6250. br.SEGMENT(headers[i].start, headers[i].length, '');
  6251. }
  6252. data = br.SEGMENT();
  6253. br.clear();
  6254. return data;
  6255. },
  6256. get: function(name) {
  6257. var array = [];
  6258. for (var i = 0, max = headers.length; i < max; i++) {
  6259. if (headers[i].name === name.toUpperCase()) {
  6260. array.push(headers[i].segment);
  6261. }
  6262. }
  6263. return array;
  6264. },
  6265. set: function(name, segment) {
  6266. var array = [], i, ii, max;
  6267. if (typeof(segment) === 'string') {
  6268. array.push(segment);
  6269. } else {
  6270. array = segment;
  6271. }
  6272. for (i = ii = 0, max = headers.length; i < max; i++) {
  6273. if (headers[i].name === name.toUpperCase()) {
  6274. headers[i].segment = array[ii];
  6275. headers[i].length = array[ii].length;
  6276. ii++;
  6277. }
  6278. if (ii >= array.length) {
  6279. break;
  6280. }
  6281. }
  6282. },
  6283. purge: function() {
  6284. this.headers = headers = [];
  6285. }
  6286. };
  6287. };
  6288. });
  6289. // Included from: src/javascript/runtime/html5/image/ExifParser.js
  6290. /**
  6291. * ExifParser.js
  6292. *
  6293. * Copyright 2013, Moxiecode Systems AB
  6294. * Released under GPL License.
  6295. *
  6296. * License: http://www.plupload.com/license
  6297. * Contributing: http://www.plupload.com/contributing
  6298. */
  6299. /**
  6300. @class moxie/runtime/html5/image/ExifParser
  6301. @private
  6302. */
  6303. define("moxie/runtime/html5/image/ExifParser", [
  6304. "moxie/core/utils/Basic",
  6305. "moxie/runtime/html5/utils/BinaryReader",
  6306. "moxie/core/Exceptions"
  6307. ], function(Basic, BinaryReader, x) {
  6308. function ExifParser(data) {
  6309. var __super__, tags, tagDescs, offsets, idx, Tiff;
  6310. BinaryReader.call(this, data);
  6311. tags = {
  6312. tiff: {
  6313. /*
  6314. The image orientation viewed in terms of rows and columns.
  6315. 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
  6316. 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
  6317. 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
  6318. 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
  6319. 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
  6320. 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
  6321. 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
  6322. 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
  6323. */
  6324. 0x0112: 'Orientation',
  6325. 0x010E: 'ImageDescription',
  6326. 0x010F: 'Make',
  6327. 0x0110: 'Model',
  6328. 0x0131: 'Software',
  6329. 0x8769: 'ExifIFDPointer',
  6330. 0x8825: 'GPSInfoIFDPointer'
  6331. },
  6332. exif: {
  6333. 0x9000: 'ExifVersion',
  6334. 0xA001: 'ColorSpace',
  6335. 0xA002: 'PixelXDimension',
  6336. 0xA003: 'PixelYDimension',
  6337. 0x9003: 'DateTimeOriginal',
  6338. 0x829A: 'ExposureTime',
  6339. 0x829D: 'FNumber',
  6340. 0x8827: 'ISOSpeedRatings',
  6341. 0x9201: 'ShutterSpeedValue',
  6342. 0x9202: 'ApertureValue' ,
  6343. 0x9207: 'MeteringMode',
  6344. 0x9208: 'LightSource',
  6345. 0x9209: 'Flash',
  6346. 0x920A: 'FocalLength',
  6347. 0xA402: 'ExposureMode',
  6348. 0xA403: 'WhiteBalance',
  6349. 0xA406: 'SceneCaptureType',
  6350. 0xA404: 'DigitalZoomRatio',
  6351. 0xA408: 'Contrast',
  6352. 0xA409: 'Saturation',
  6353. 0xA40A: 'Sharpness'
  6354. },
  6355. gps: {
  6356. 0x0000: 'GPSVersionID',
  6357. 0x0001: 'GPSLatitudeRef',
  6358. 0x0002: 'GPSLatitude',
  6359. 0x0003: 'GPSLongitudeRef',
  6360. 0x0004: 'GPSLongitude'
  6361. },
  6362. thumb: {
  6363. 0x0201: 'JPEGInterchangeFormat',
  6364. 0x0202: 'JPEGInterchangeFormatLength'
  6365. }
  6366. };
  6367. tagDescs = {
  6368. 'ColorSpace': {
  6369. 1: 'sRGB',
  6370. 0: 'Uncalibrated'
  6371. },
  6372. 'MeteringMode': {
  6373. 0: 'Unknown',
  6374. 1: 'Average',
  6375. 2: 'CenterWeightedAverage',
  6376. 3: 'Spot',
  6377. 4: 'MultiSpot',
  6378. 5: 'Pattern',
  6379. 6: 'Partial',
  6380. 255: 'Other'
  6381. },
  6382. 'LightSource': {
  6383. 1: 'Daylight',
  6384. 2: 'Fliorescent',
  6385. 3: 'Tungsten',
  6386. 4: 'Flash',
  6387. 9: 'Fine weather',
  6388. 10: 'Cloudy weather',
  6389. 11: 'Shade',
  6390. 12: 'Daylight fluorescent (D 5700 - 7100K)',
  6391. 13: 'Day white fluorescent (N 4600 -5400K)',
  6392. 14: 'Cool white fluorescent (W 3900 - 4500K)',
  6393. 15: 'White fluorescent (WW 3200 - 3700K)',
  6394. 17: 'Standard light A',
  6395. 18: 'Standard light B',
  6396. 19: 'Standard light C',
  6397. 20: 'D55',
  6398. 21: 'D65',
  6399. 22: 'D75',
  6400. 23: 'D50',
  6401. 24: 'ISO studio tungsten',
  6402. 255: 'Other'
  6403. },
  6404. 'Flash': {
  6405. 0x0000: 'Flash did not fire',
  6406. 0x0001: 'Flash fired',
  6407. 0x0005: 'Strobe return light not detected',
  6408. 0x0007: 'Strobe return light detected',
  6409. 0x0009: 'Flash fired, compulsory flash mode',
  6410. 0x000D: 'Flash fired, compulsory flash mode, return light not detected',
  6411. 0x000F: 'Flash fired, compulsory flash mode, return light detected',
  6412. 0x0010: 'Flash did not fire, compulsory flash mode',
  6413. 0x0018: 'Flash did not fire, auto mode',
  6414. 0x0019: 'Flash fired, auto mode',
  6415. 0x001D: 'Flash fired, auto mode, return light not detected',
  6416. 0x001F: 'Flash fired, auto mode, return light detected',
  6417. 0x0020: 'No flash function',
  6418. 0x0041: 'Flash fired, red-eye reduction mode',
  6419. 0x0045: 'Flash fired, red-eye reduction mode, return light not detected',
  6420. 0x0047: 'Flash fired, red-eye reduction mode, return light detected',
  6421. 0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',
  6422. 0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
  6423. 0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
  6424. 0x0059: 'Flash fired, auto mode, red-eye reduction mode',
  6425. 0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
  6426. 0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
  6427. },
  6428. 'ExposureMode': {
  6429. 0: 'Auto exposure',
  6430. 1: 'Manual exposure',
  6431. 2: 'Auto bracket'
  6432. },
  6433. 'WhiteBalance': {
  6434. 0: 'Auto white balance',
  6435. 1: 'Manual white balance'
  6436. },
  6437. 'SceneCaptureType': {
  6438. 0: 'Standard',
  6439. 1: 'Landscape',
  6440. 2: 'Portrait',
  6441. 3: 'Night scene'
  6442. },
  6443. 'Contrast': {
  6444. 0: 'Normal',
  6445. 1: 'Soft',
  6446. 2: 'Hard'
  6447. },
  6448. 'Saturation': {
  6449. 0: 'Normal',
  6450. 1: 'Low saturation',
  6451. 2: 'High saturation'
  6452. },
  6453. 'Sharpness': {
  6454. 0: 'Normal',
  6455. 1: 'Soft',
  6456. 2: 'Hard'
  6457. },
  6458. // GPS related
  6459. 'GPSLatitudeRef': {
  6460. N: 'North latitude',
  6461. S: 'South latitude'
  6462. },
  6463. 'GPSLongitudeRef': {
  6464. E: 'East longitude',
  6465. W: 'West longitude'
  6466. }
  6467. };
  6468. offsets = {
  6469. tiffHeader: 10
  6470. };
  6471. idx = offsets.tiffHeader;
  6472. __super__ = {
  6473. clear: this.clear
  6474. };
  6475. // Public functions
  6476. Basic.extend(this, {
  6477. read: function() {
  6478. try {
  6479. return ExifParser.prototype.read.apply(this, arguments);
  6480. } catch (ex) {
  6481. throw new x.ImageError(x.ImageError.INVALID_META_ERR);
  6482. }
  6483. },
  6484. write: function() {
  6485. try {
  6486. return ExifParser.prototype.write.apply(this, arguments);
  6487. } catch (ex) {
  6488. throw new x.ImageError(x.ImageError.INVALID_META_ERR);
  6489. }
  6490. },
  6491. UNDEFINED: function() {
  6492. return this.BYTE.apply(this, arguments);
  6493. },
  6494. RATIONAL: function(idx) {
  6495. return this.LONG(idx) / this.LONG(idx + 4)
  6496. },
  6497. SRATIONAL: function(idx) {
  6498. return this.SLONG(idx) / this.SLONG(idx + 4)
  6499. },
  6500. ASCII: function(idx) {
  6501. return this.CHAR(idx);
  6502. },
  6503. TIFF: function() {
  6504. return Tiff || null;
  6505. },
  6506. EXIF: function() {
  6507. var Exif = null;
  6508. if (offsets.exifIFD) {
  6509. try {
  6510. Exif = extractTags.call(this, offsets.exifIFD, tags.exif);
  6511. } catch(ex) {
  6512. return null;
  6513. }
  6514. // Fix formatting of some tags
  6515. if (Exif.ExifVersion && Basic.typeOf(Exif.ExifVersion) === 'array') {
  6516. for (var i = 0, exifVersion = ''; i < Exif.ExifVersion.length; i++) {
  6517. exifVersion += String.fromCharCode(Exif.ExifVersion[i]);
  6518. }
  6519. Exif.ExifVersion = exifVersion;
  6520. }
  6521. }
  6522. return Exif;
  6523. },
  6524. GPS: function() {
  6525. var GPS = null;
  6526. if (offsets.gpsIFD) {
  6527. try {
  6528. GPS = extractTags.call(this, offsets.gpsIFD, tags.gps);
  6529. } catch (ex) {
  6530. return null;
  6531. }
  6532. // iOS devices (and probably some others) do not put in GPSVersionID tag (why?..)
  6533. if (GPS.GPSVersionID && Basic.typeOf(GPS.GPSVersionID) === 'array') {
  6534. GPS.GPSVersionID = GPS.GPSVersionID.join('.');
  6535. }
  6536. }
  6537. return GPS;
  6538. },
  6539. thumb: function() {
  6540. if (offsets.IFD1) {
  6541. try {
  6542. var IFD1Tags = extractTags.call(this, offsets.IFD1, tags.thumb);
  6543. if ('JPEGInterchangeFormat' in IFD1Tags) {
  6544. return this.SEGMENT(offsets.tiffHeader + IFD1Tags.JPEGInterchangeFormat, IFD1Tags.JPEGInterchangeFormatLength);
  6545. }
  6546. } catch (ex) {}
  6547. }
  6548. return null;
  6549. },
  6550. setExif: function(tag, value) {
  6551. // Right now only setting of width/height is possible
  6552. if (tag !== 'PixelXDimension' && tag !== 'PixelYDimension') { return false; }
  6553. return setTag.call(this, 'exif', tag, value);
  6554. },
  6555. clear: function() {
  6556. __super__.clear();
  6557. data = tags = tagDescs = Tiff = offsets = __super__ = null;
  6558. }
  6559. });
  6560. // Check if that's APP1 and that it has EXIF
  6561. if (this.SHORT(0) !== 0xFFE1 || this.STRING(4, 5).toUpperCase() !== "EXIF\0") {
  6562. throw new x.ImageError(x.ImageError.INVALID_META_ERR);
  6563. }
  6564. // Set read order of multi-byte data
  6565. this.littleEndian = (this.SHORT(idx) == 0x4949);
  6566. // Check if always present bytes are indeed present
  6567. if (this.SHORT(idx+=2) !== 0x002A) {
  6568. throw new x.ImageError(x.ImageError.INVALID_META_ERR);
  6569. }
  6570. offsets.IFD0 = offsets.tiffHeader + this.LONG(idx += 2);
  6571. Tiff = extractTags.call(this, offsets.IFD0, tags.tiff);
  6572. if ('ExifIFDPointer' in Tiff) {
  6573. offsets.exifIFD = offsets.tiffHeader + Tiff.ExifIFDPointer;
  6574. delete Tiff.ExifIFDPointer;
  6575. }
  6576. if ('GPSInfoIFDPointer' in Tiff) {
  6577. offsets.gpsIFD = offsets.tiffHeader + Tiff.GPSInfoIFDPointer;
  6578. delete Tiff.GPSInfoIFDPointer;
  6579. }
  6580. if (Basic.isEmptyObj(Tiff)) {
  6581. Tiff = null;
  6582. }
  6583. // check if we have a thumb as well
  6584. var IFD1Offset = this.LONG(offsets.IFD0 + this.SHORT(offsets.IFD0) * 12 + 2);
  6585. if (IFD1Offset) {
  6586. offsets.IFD1 = offsets.tiffHeader + IFD1Offset;
  6587. }
  6588. function extractTags(IFD_offset, tags2extract) {
  6589. var data = this;
  6590. var length, i, tag, type, count, size, offset, value, values = [], hash = {};
  6591. var types = {
  6592. 1 : 'BYTE',
  6593. 7 : 'UNDEFINED',
  6594. 2 : 'ASCII',
  6595. 3 : 'SHORT',
  6596. 4 : 'LONG',
  6597. 5 : 'RATIONAL',
  6598. 9 : 'SLONG',
  6599. 10: 'SRATIONAL'
  6600. };
  6601. var sizes = {
  6602. 'BYTE' : 1,
  6603. 'UNDEFINED' : 1,
  6604. 'ASCII' : 1,
  6605. 'SHORT' : 2,
  6606. 'LONG' : 4,
  6607. 'RATIONAL' : 8,
  6608. 'SLONG' : 4,
  6609. 'SRATIONAL' : 8
  6610. };
  6611. length = data.SHORT(IFD_offset);
  6612. // The size of APP1 including all these elements shall not exceed the 64 Kbytes specified in the JPEG standard.
  6613. for (i = 0; i < length; i++) {
  6614. values = [];
  6615. // Set binary reader pointer to beginning of the next tag
  6616. offset = IFD_offset + 2 + i*12;
  6617. tag = tags2extract[data.SHORT(offset)];
  6618. if (tag === undefined) {
  6619. continue; // Not the tag we requested
  6620. }
  6621. type = types[data.SHORT(offset+=2)];
  6622. count = data.LONG(offset+=2);
  6623. size = sizes[type];
  6624. if (!size) {
  6625. throw new x.ImageError(x.ImageError.INVALID_META_ERR);
  6626. }
  6627. offset += 4;
  6628. // tag can only fit 4 bytes of data, if data is larger we should look outside
  6629. if (size * count > 4) {
  6630. // instead of data tag contains an offset of the data
  6631. offset = data.LONG(offset) + offsets.tiffHeader;
  6632. }
  6633. // in case we left the boundaries of data throw an early exception
  6634. if (offset + size * count >= this.length()) {
  6635. throw new x.ImageError(x.ImageError.INVALID_META_ERR);
  6636. }
  6637. // special care for the string
  6638. if (type === 'ASCII') {
  6639. hash[tag] = Basic.trim(data.STRING(offset, count).replace(/\0$/, '')); // strip trailing NULL
  6640. continue;
  6641. } else {
  6642. values = data.asArray(type, offset, count);
  6643. value = (count == 1 ? values[0] : values);
  6644. if (tagDescs.hasOwnProperty(tag) && typeof value != 'object') {
  6645. hash[tag] = tagDescs[tag][value];
  6646. } else {
  6647. hash[tag] = value;
  6648. }
  6649. }
  6650. }
  6651. return hash;
  6652. }
  6653. // At the moment only setting of simple (LONG) values, that do not require offset recalculation, is supported
  6654. function setTag(ifd, tag, value) {
  6655. var offset, length, tagOffset, valueOffset = 0;
  6656. // If tag name passed translate into hex key
  6657. if (typeof(tag) === 'string') {
  6658. var tmpTags = tags[ifd.toLowerCase()];
  6659. for (var hex in tmpTags) {
  6660. if (tmpTags[hex] === tag) {
  6661. tag = hex;
  6662. break;
  6663. }
  6664. }
  6665. }
  6666. offset = offsets[ifd.toLowerCase() + 'IFD'];
  6667. length = this.SHORT(offset);
  6668. for (var i = 0; i < length; i++) {
  6669. tagOffset = offset + 12 * i + 2;
  6670. if (this.SHORT(tagOffset) == tag) {
  6671. valueOffset = tagOffset + 8;
  6672. break;
  6673. }
  6674. }
  6675. if (!valueOffset) {
  6676. return false;
  6677. }
  6678. try {
  6679. this.write(valueOffset, value, 4);
  6680. } catch(ex) {
  6681. return false;
  6682. }
  6683. return true;
  6684. }
  6685. }
  6686. ExifParser.prototype = BinaryReader.prototype;
  6687. return ExifParser;
  6688. });
  6689. // Included from: src/javascript/runtime/html5/image/JPEG.js
  6690. /**
  6691. * JPEG.js
  6692. *
  6693. * Copyright 2013, Moxiecode Systems AB
  6694. * Released under GPL License.
  6695. *
  6696. * License: http://www.plupload.com/license
  6697. * Contributing: http://www.plupload.com/contributing
  6698. */
  6699. /**
  6700. @class moxie/runtime/html5/image/JPEG
  6701. @private
  6702. */
  6703. define("moxie/runtime/html5/image/JPEG", [
  6704. "moxie/core/utils/Basic",
  6705. "moxie/core/Exceptions",
  6706. "moxie/runtime/html5/image/JPEGHeaders",
  6707. "moxie/runtime/html5/utils/BinaryReader",
  6708. "moxie/runtime/html5/image/ExifParser"
  6709. ], function(Basic, x, JPEGHeaders, BinaryReader, ExifParser) {
  6710. function JPEG(data) {
  6711. var _br, _hm, _ep, _info;
  6712. _br = new BinaryReader(data);
  6713. // check if it is jpeg
  6714. if (_br.SHORT(0) !== 0xFFD8) {
  6715. throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  6716. }
  6717. // backup headers
  6718. _hm = new JPEGHeaders(data);
  6719. // extract exif info
  6720. try {
  6721. _ep = new ExifParser(_hm.get('app1')[0]);
  6722. } catch(ex) {}
  6723. // get dimensions
  6724. _info = _getDimensions.call(this);
  6725. Basic.extend(this, {
  6726. type: 'image/jpeg',
  6727. size: _br.length(),
  6728. width: _info && _info.width || 0,
  6729. height: _info && _info.height || 0,
  6730. setExif: function(tag, value) {
  6731. if (!_ep) {
  6732. return false; // or throw an exception
  6733. }
  6734. if (Basic.typeOf(tag) === 'object') {
  6735. Basic.each(tag, function(value, tag) {
  6736. _ep.setExif(tag, value);
  6737. });
  6738. } else {
  6739. _ep.setExif(tag, value);
  6740. }
  6741. // update internal headers
  6742. _hm.set('app1', _ep.SEGMENT());
  6743. },
  6744. writeHeaders: function() {
  6745. if (!arguments.length) {
  6746. // if no arguments passed, update headers internally
  6747. return _hm.restore(data);
  6748. }
  6749. return _hm.restore(arguments[0]);
  6750. },
  6751. stripHeaders: function(data) {
  6752. return _hm.strip(data);
  6753. },
  6754. purge: function() {
  6755. _purge.call(this);
  6756. }
  6757. });
  6758. if (_ep) {
  6759. this.meta = {
  6760. tiff: _ep.TIFF(),
  6761. exif: _ep.EXIF(),
  6762. gps: _ep.GPS(),
  6763. thumb: _getThumb()
  6764. };
  6765. }
  6766. function _getDimensions(br) {
  6767. var idx = 0
  6768. , marker
  6769. , length
  6770. ;
  6771. if (!br) {
  6772. br = _br;
  6773. }
  6774. // examine all through the end, since some images might have very large APP segments
  6775. while (idx <= br.length()) {
  6776. marker = br.SHORT(idx += 2);
  6777. if (marker >= 0xFFC0 && marker <= 0xFFC3) { // SOFn
  6778. idx += 5; // marker (2 bytes) + length (2 bytes) + Sample precision (1 byte)
  6779. return {
  6780. height: br.SHORT(idx),
  6781. width: br.SHORT(idx += 2)
  6782. };
  6783. }
  6784. length = br.SHORT(idx += 2);
  6785. idx += length - 2;
  6786. }
  6787. return null;
  6788. }
  6789. function _getThumb() {
  6790. var data = _ep.thumb()
  6791. , br
  6792. , info
  6793. ;
  6794. if (data) {
  6795. br = new BinaryReader(data);
  6796. info = _getDimensions(br);
  6797. br.clear();
  6798. if (info) {
  6799. info.data = data;
  6800. return info;
  6801. }
  6802. }
  6803. return null;
  6804. }
  6805. function _purge() {
  6806. if (!_ep || !_hm || !_br) {
  6807. return; // ignore any repeating purge requests
  6808. }
  6809. _ep.clear();
  6810. _hm.purge();
  6811. _br.clear();
  6812. _info = _hm = _ep = _br = null;
  6813. }
  6814. }
  6815. return JPEG;
  6816. });
  6817. // Included from: src/javascript/runtime/html5/image/PNG.js
  6818. /**
  6819. * PNG.js
  6820. *
  6821. * Copyright 2013, Moxiecode Systems AB
  6822. * Released under GPL License.
  6823. *
  6824. * License: http://www.plupload.com/license
  6825. * Contributing: http://www.plupload.com/contributing
  6826. */
  6827. /**
  6828. @class moxie/runtime/html5/image/PNG
  6829. @private
  6830. */
  6831. define("moxie/runtime/html5/image/PNG", [
  6832. "moxie/core/Exceptions",
  6833. "moxie/core/utils/Basic",
  6834. "moxie/runtime/html5/utils/BinaryReader"
  6835. ], function(x, Basic, BinaryReader) {
  6836. function PNG(data) {
  6837. var _br, _hm, _ep, _info;
  6838. _br = new BinaryReader(data);
  6839. // check if it's png
  6840. (function() {
  6841. var idx = 0, i = 0
  6842. , signature = [0x8950, 0x4E47, 0x0D0A, 0x1A0A]
  6843. ;
  6844. for (i = 0; i < signature.length; i++, idx += 2) {
  6845. if (signature[i] != _br.SHORT(idx)) {
  6846. throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  6847. }
  6848. }
  6849. }());
  6850. function _getDimensions() {
  6851. var chunk, idx;
  6852. chunk = _getChunkAt.call(this, 8);
  6853. if (chunk.type == 'IHDR') {
  6854. idx = chunk.start;
  6855. return {
  6856. width: _br.LONG(idx),
  6857. height: _br.LONG(idx += 4)
  6858. };
  6859. }
  6860. return null;
  6861. }
  6862. function _purge() {
  6863. if (!_br) {
  6864. return; // ignore any repeating purge requests
  6865. }
  6866. _br.clear();
  6867. data = _info = _hm = _ep = _br = null;
  6868. }
  6869. _info = _getDimensions.call(this);
  6870. Basic.extend(this, {
  6871. type: 'image/png',
  6872. size: _br.length(),
  6873. width: _info.width,
  6874. height: _info.height,
  6875. purge: function() {
  6876. _purge.call(this);
  6877. }
  6878. });
  6879. // for PNG we can safely trigger purge automatically, as we do not keep any data for later
  6880. _purge.call(this);
  6881. function _getChunkAt(idx) {
  6882. var length, type, start, CRC;
  6883. length = _br.LONG(idx);
  6884. type = _br.STRING(idx += 4, 4);
  6885. start = idx += 4;
  6886. CRC = _br.LONG(idx + length);
  6887. return {
  6888. length: length,
  6889. type: type,
  6890. start: start,
  6891. CRC: CRC
  6892. };
  6893. }
  6894. }
  6895. return PNG;
  6896. });
  6897. // Included from: src/javascript/runtime/html5/image/ImageInfo.js
  6898. /**
  6899. * ImageInfo.js
  6900. *
  6901. * Copyright 2013, Moxiecode Systems AB
  6902. * Released under GPL License.
  6903. *
  6904. * License: http://www.plupload.com/license
  6905. * Contributing: http://www.plupload.com/contributing
  6906. */
  6907. /**
  6908. @class moxie/runtime/html5/image/ImageInfo
  6909. @private
  6910. */
  6911. define("moxie/runtime/html5/image/ImageInfo", [
  6912. "moxie/core/utils/Basic",
  6913. "moxie/core/Exceptions",
  6914. "moxie/runtime/html5/image/JPEG",
  6915. "moxie/runtime/html5/image/PNG"
  6916. ], function(Basic, x, JPEG, PNG) {
  6917. /**
  6918. Optional image investigation tool for HTML5 runtime. Provides the following features:
  6919. - ability to distinguish image type (JPEG or PNG) by signature
  6920. - ability to extract image width/height directly from it's internals, without preloading in memory (fast)
  6921. - ability to extract APP headers from JPEGs (Exif, GPS, etc)
  6922. - ability to replace width/height tags in extracted JPEG headers
  6923. - ability to restore APP headers, that were for example stripped during image manipulation
  6924. @class ImageInfo
  6925. @constructor
  6926. @param {String} data Image source as binary string
  6927. */
  6928. return function(data) {
  6929. var _cs = [JPEG, PNG], _img;
  6930. // figure out the format, throw: ImageError.WRONG_FORMAT if not supported
  6931. _img = (function() {
  6932. for (var i = 0; i < _cs.length; i++) {
  6933. try {
  6934. return new _cs[i](data);
  6935. } catch (ex) {
  6936. // console.info(ex);
  6937. }
  6938. }
  6939. throw new x.ImageError(x.ImageError.WRONG_FORMAT);
  6940. }());
  6941. Basic.extend(this, {
  6942. /**
  6943. Image Mime Type extracted from it's depths
  6944. @property type
  6945. @type {String}
  6946. @default ''
  6947. */
  6948. type: '',
  6949. /**
  6950. Image size in bytes
  6951. @property size
  6952. @type {Number}
  6953. @default 0
  6954. */
  6955. size: 0,
  6956. /**
  6957. Image width extracted from image source
  6958. @property width
  6959. @type {Number}
  6960. @default 0
  6961. */
  6962. width: 0,
  6963. /**
  6964. Image height extracted from image source
  6965. @property height
  6966. @type {Number}
  6967. @default 0
  6968. */
  6969. height: 0,
  6970. /**
  6971. Sets Exif tag. Currently applicable only for width and height tags. Obviously works only with JPEGs.
  6972. @method setExif
  6973. @param {String} tag Tag to set
  6974. @param {Mixed} value Value to assign to the tag
  6975. */
  6976. setExif: function() {},
  6977. /**
  6978. Restores headers to the source.
  6979. @method writeHeaders
  6980. @param {String} data Image source as binary string
  6981. @return {String} Updated binary string
  6982. */
  6983. writeHeaders: function(data) {
  6984. return data;
  6985. },
  6986. /**
  6987. Strip all headers from the source.
  6988. @method stripHeaders
  6989. @param {String} data Image source as binary string
  6990. @return {String} Updated binary string
  6991. */
  6992. stripHeaders: function(data) {
  6993. return data;
  6994. },
  6995. /**
  6996. Dispose resources.
  6997. @method purge
  6998. */
  6999. purge: function() {
  7000. data = null;
  7001. }
  7002. });
  7003. Basic.extend(this, _img);
  7004. this.purge = function() {
  7005. _img.purge();
  7006. _img = null;
  7007. };
  7008. };
  7009. });
  7010. // Included from: src/javascript/runtime/html5/image/MegaPixel.js
  7011. /**
  7012. (The MIT License)
  7013. Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>;
  7014. Permission is hereby granted, free of charge, to any person obtaining
  7015. a copy of this software and associated documentation files (the
  7016. 'Software'), to deal in the Software without restriction, including
  7017. without limitation the rights to use, copy, modify, merge, publish,
  7018. distribute, sublicense, and/or sell copies of the Software, and to
  7019. permit persons to whom the Software is furnished to do so, subject to
  7020. the following conditions:
  7021. The above copyright notice and this permission notice shall be
  7022. included in all copies or substantial portions of the Software.
  7023. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
  7024. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  7025. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  7026. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  7027. CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  7028. TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  7029. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  7030. */
  7031. /**
  7032. * Mega pixel image rendering library for iOS6 Safari
  7033. *
  7034. * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel),
  7035. * which causes unexpected subsampling when drawing it in canvas.
  7036. * By using this library, you can safely render the image with proper stretching.
  7037. *
  7038. * Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>
  7039. * Released under the MIT license
  7040. */
  7041. /**
  7042. @class moxie/runtime/html5/image/MegaPixel
  7043. @private
  7044. */
  7045. define("moxie/runtime/html5/image/MegaPixel", [], function() {
  7046. /**
  7047. * Rendering image element (with resizing) into the canvas element
  7048. */
  7049. function renderImageToCanvas(img, canvas, options) {
  7050. var iw = img.naturalWidth, ih = img.naturalHeight;
  7051. var width = options.width, height = options.height;
  7052. var x = options.x || 0, y = options.y || 0;
  7053. var ctx = canvas.getContext('2d');
  7054. if (detectSubsampling(img)) {
  7055. iw /= 2;
  7056. ih /= 2;
  7057. }
  7058. var d = 1024; // size of tiling canvas
  7059. var tmpCanvas = document.createElement('canvas');
  7060. tmpCanvas.width = tmpCanvas.height = d;
  7061. var tmpCtx = tmpCanvas.getContext('2d');
  7062. var vertSquashRatio = detectVerticalSquash(img, iw, ih);
  7063. var sy = 0;
  7064. while (sy < ih) {
  7065. var sh = sy + d > ih ? ih - sy : d;
  7066. var sx = 0;
  7067. while (sx < iw) {
  7068. var sw = sx + d > iw ? iw - sx : d;
  7069. tmpCtx.clearRect(0, 0, d, d);
  7070. tmpCtx.drawImage(img, -sx, -sy);
  7071. var dx = (sx * width / iw + x) << 0;
  7072. var dw = Math.ceil(sw * width / iw);
  7073. var dy = (sy * height / ih / vertSquashRatio + y) << 0;
  7074. var dh = Math.ceil(sh * height / ih / vertSquashRatio);
  7075. ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
  7076. sx += d;
  7077. }
  7078. sy += d;
  7079. }
  7080. tmpCanvas = tmpCtx = null;
  7081. }
  7082. /**
  7083. * Detect subsampling in loaded image.
  7084. * In iOS, larger images than 2M pixels may be subsampled in rendering.
  7085. */
  7086. function detectSubsampling(img) {
  7087. var iw = img.naturalWidth, ih = img.naturalHeight;
  7088. if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
  7089. var canvas = document.createElement('canvas');
  7090. canvas.width = canvas.height = 1;
  7091. var ctx = canvas.getContext('2d');
  7092. ctx.drawImage(img, -iw + 1, 0);
  7093. // subsampled image becomes half smaller in rendering size.
  7094. // check alpha channel value to confirm image is covering edge pixel or not.
  7095. // if alpha value is 0 image is not covering, hence subsampled.
  7096. return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
  7097. } else {
  7098. return false;
  7099. }
  7100. }
  7101. /**
  7102. * Detecting vertical squash in loaded image.
  7103. * Fixes a bug which squash image vertically while drawing into canvas for some images.
  7104. */
  7105. function detectVerticalSquash(img, iw, ih) {
  7106. var canvas = document.createElement('canvas');
  7107. canvas.width = 1;
  7108. canvas.height = ih;
  7109. var ctx = canvas.getContext('2d');
  7110. ctx.drawImage(img, 0, 0);
  7111. var data = ctx.getImageData(0, 0, 1, ih).data;
  7112. // search image edge pixel position in case it is squashed vertically.
  7113. var sy = 0;
  7114. var ey = ih;
  7115. var py = ih;
  7116. while (py > sy) {
  7117. var alpha = data[(py - 1) * 4 + 3];
  7118. if (alpha === 0) {
  7119. ey = py;
  7120. } else {
  7121. sy = py;
  7122. }
  7123. py = (ey + sy) >> 1;
  7124. }
  7125. canvas = null;
  7126. var ratio = (py / ih);
  7127. return (ratio === 0) ? 1 : ratio;
  7128. }
  7129. return {
  7130. isSubsampled: detectSubsampling,
  7131. renderTo: renderImageToCanvas
  7132. };
  7133. });
  7134. // Included from: src/javascript/runtime/html5/image/Image.js
  7135. /**
  7136. * Image.js
  7137. *
  7138. * Copyright 2013, Moxiecode Systems AB
  7139. * Released under GPL License.
  7140. *
  7141. * License: http://www.plupload.com/license
  7142. * Contributing: http://www.plupload.com/contributing
  7143. */
  7144. /**
  7145. @class moxie/runtime/html5/image/Image
  7146. @private
  7147. */
  7148. define("moxie/runtime/html5/image/Image", [
  7149. "moxie/runtime/html5/Runtime",
  7150. "moxie/core/utils/Basic",
  7151. "moxie/core/Exceptions",
  7152. "moxie/core/utils/Encode",
  7153. "moxie/file/Blob",
  7154. "moxie/file/File",
  7155. "moxie/runtime/html5/image/ImageInfo",
  7156. "moxie/runtime/html5/image/MegaPixel",
  7157. "moxie/core/utils/Mime",
  7158. "moxie/core/utils/Env"
  7159. ], function(extensions, Basic, x, Encode, Blob, File, ImageInfo, MegaPixel, Mime, Env) {
  7160. function HTML5Image() {
  7161. var me = this
  7162. , _img, _imgInfo, _canvas, _binStr, _blob
  7163. , _modified = false // is set true whenever image is modified
  7164. , _preserveHeaders = true
  7165. ;
  7166. Basic.extend(this, {
  7167. loadFromBlob: function(blob) {
  7168. var comp = this, I = comp.getRuntime()
  7169. , asBinary = arguments.length > 1 ? arguments[1] : true
  7170. ;
  7171. if (!I.can('access_binary')) {
  7172. throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
  7173. }
  7174. _blob = blob;
  7175. if (blob.isDetached()) {
  7176. _binStr = blob.getSource();
  7177. _preload.call(this, _binStr);
  7178. return;
  7179. } else {
  7180. _readAsDataUrl.call(this, blob.getSource(), function(dataUrl) {
  7181. if (asBinary) {
  7182. _binStr = _toBinary(dataUrl);
  7183. }
  7184. _preload.call(comp, dataUrl);
  7185. });
  7186. }
  7187. },
  7188. loadFromImage: function(img, exact) {
  7189. this.meta = img.meta;
  7190. _blob = new File(null, {
  7191. name: img.name,
  7192. size: img.size,
  7193. type: img.type
  7194. });
  7195. _preload.call(this, exact ? (_binStr = img.getAsBinaryString()) : img.getAsDataURL());
  7196. },
  7197. getInfo: function() {
  7198. var I = this.getRuntime(), info;
  7199. if (!_imgInfo && _binStr && I.can('access_image_binary')) {
  7200. _imgInfo = new ImageInfo(_binStr);
  7201. }
  7202. info = {
  7203. width: _getImg().width || 0,
  7204. height: _getImg().height || 0,
  7205. type: _blob.type || Mime.getFileMime(_blob.name),
  7206. size: _binStr && _binStr.length || _blob.size || 0,
  7207. name: _blob.name || '',
  7208. meta: _imgInfo && _imgInfo.meta || this.meta || {}
  7209. };
  7210. // store thumbnail data as blob
  7211. if (info.meta && info.meta.thumb && !(info.meta.thumb.data instanceof Blob)) {
  7212. info.meta.thumb.data = new Blob(null, {
  7213. type: 'image/jpeg',
  7214. data: info.meta.thumb.data
  7215. });
  7216. }
  7217. return info;
  7218. },
  7219. downsize: function() {
  7220. _downsize.apply(this, arguments);
  7221. },
  7222. getAsCanvas: function() {
  7223. if (_canvas) {
  7224. _canvas.id = this.uid + '_canvas';
  7225. }
  7226. return _canvas;
  7227. },
  7228. getAsBlob: function(type, quality) {
  7229. if (type !== this.type) {
  7230. // if different mime type requested prepare image for conversion
  7231. _downsize.call(this, this.width, this.height, false);
  7232. }
  7233. return new File(null, {
  7234. name: _blob.name || '',
  7235. type: type,
  7236. data: me.getAsBinaryString.call(this, type, quality)
  7237. });
  7238. },
  7239. getAsDataURL: function(type) {
  7240. var quality = arguments[1] || 90;
  7241. // if image has not been modified, return the source right away
  7242. if (!_modified) {
  7243. return _img.src;
  7244. }
  7245. if ('image/jpeg' !== type) {
  7246. return _canvas.toDataURL('image/png');
  7247. } else {
  7248. try {
  7249. // older Geckos used to result in an exception on quality argument
  7250. return _canvas.toDataURL('image/jpeg', quality/100);
  7251. } catch (ex) {
  7252. return _canvas.toDataURL('image/jpeg');
  7253. }
  7254. }
  7255. },
  7256. getAsBinaryString: function(type, quality) {
  7257. // if image has not been modified, return the source right away
  7258. if (!_modified) {
  7259. // if image was not loaded from binary string
  7260. if (!_binStr) {
  7261. _binStr = _toBinary(me.getAsDataURL(type, quality));
  7262. }
  7263. return _binStr;
  7264. }
  7265. if ('image/jpeg' !== type) {
  7266. _binStr = _toBinary(me.getAsDataURL(type, quality));
  7267. } else {
  7268. var dataUrl;
  7269. // if jpeg
  7270. if (!quality) {
  7271. quality = 90;
  7272. }
  7273. try {
  7274. // older Geckos used to result in an exception on quality argument
  7275. dataUrl = _canvas.toDataURL('image/jpeg', quality/100);
  7276. } catch (ex) {
  7277. dataUrl = _canvas.toDataURL('image/jpeg');
  7278. }
  7279. _binStr = _toBinary(dataUrl);
  7280. if (_imgInfo) {
  7281. _binStr = _imgInfo.stripHeaders(_binStr);
  7282. if (_preserveHeaders) {
  7283. // update dimensions info in exif
  7284. if (_imgInfo.meta && _imgInfo.meta.exif) {
  7285. _imgInfo.setExif({
  7286. PixelXDimension: this.width,
  7287. PixelYDimension: this.height
  7288. });
  7289. }
  7290. // re-inject the headers
  7291. _binStr = _imgInfo.writeHeaders(_binStr);
  7292. }
  7293. // will be re-created from fresh on next getInfo call
  7294. _imgInfo.purge();
  7295. _imgInfo = null;
  7296. }
  7297. }
  7298. _modified = false;
  7299. return _binStr;
  7300. },
  7301. destroy: function() {
  7302. me = null;
  7303. _purge.call(this);
  7304. this.getRuntime().getShim().removeInstance(this.uid);
  7305. }
  7306. });
  7307. function _getImg() {
  7308. if (!_canvas && !_img) {
  7309. throw new x.ImageError(x.DOMException.INVALID_STATE_ERR);
  7310. }
  7311. return _canvas || _img;
  7312. }
  7313. function _toBinary(str) {
  7314. return Encode.atob(str.substring(str.indexOf('base64,') + 7));
  7315. }
  7316. function _toDataUrl(str, type) {
  7317. return 'data:' + (type || '') + ';base64,' + Encode.btoa(str);
  7318. }
  7319. function _preload(str) {
  7320. var comp = this;
  7321. _img = new Image();
  7322. _img.onerror = function() {
  7323. _purge.call(this);
  7324. comp.trigger('error', x.ImageError.WRONG_FORMAT);
  7325. };
  7326. _img.onload = function() {
  7327. comp.trigger('load');
  7328. };
  7329. _img.src = str.substr(0, 5) == 'data:' ? str : _toDataUrl(str, _blob.type);
  7330. }
  7331. function _readAsDataUrl(file, callback) {
  7332. var comp = this, fr;
  7333. // use FileReader if it's available
  7334. if (window.FileReader) {
  7335. fr = new FileReader();
  7336. fr.onload = function() {
  7337. callback(this.result);
  7338. };
  7339. fr.onerror = function() {
  7340. comp.trigger('error', x.ImageError.WRONG_FORMAT);
  7341. };
  7342. fr.readAsDataURL(file);
  7343. } else {
  7344. return callback(file.getAsDataURL());
  7345. }
  7346. }
  7347. function _downsize(width, height, crop, preserveHeaders) {
  7348. var self = this
  7349. , scale
  7350. , mathFn
  7351. , x = 0
  7352. , y = 0
  7353. , img
  7354. , destWidth
  7355. , destHeight
  7356. , orientation
  7357. ;
  7358. _preserveHeaders = preserveHeaders; // we will need to check this on export (see getAsBinaryString())
  7359. // take into account orientation tag
  7360. orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
  7361. if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation
  7362. // swap dimensions
  7363. var tmp = width;
  7364. width = height;
  7365. height = tmp;
  7366. }
  7367. img = _getImg();
  7368. // unify dimensions
  7369. if (!crop) {
  7370. scale = Math.min(width/img.width, height/img.height);
  7371. } else {
  7372. // one of the dimensions may exceed the actual image dimensions - we need to take the smallest value
  7373. width = Math.min(width, img.width);
  7374. height = Math.min(height, img.height);
  7375. scale = Math.max(width/img.width, height/img.height);
  7376. }
  7377. // we only downsize here
  7378. if (scale > 1 && !crop && preserveHeaders) {
  7379. this.trigger('Resize');
  7380. return;
  7381. }
  7382. // prepare canvas if necessary
  7383. if (!_canvas) {
  7384. _canvas = document.createElement("canvas");
  7385. }
  7386. // calculate dimensions of proportionally resized image
  7387. destWidth = Math.round(img.width * scale);
  7388. destHeight = Math.round(img.height * scale);
  7389. // scale image and canvas
  7390. if (crop) {
  7391. _canvas.width = width;
  7392. _canvas.height = height;
  7393. // if dimensions of the resulting image still larger than canvas, center it
  7394. if (destWidth > width) {
  7395. x = Math.round((destWidth - width) / 2);
  7396. }
  7397. if (destHeight > height) {
  7398. y = Math.round((destHeight - height) / 2);
  7399. }
  7400. } else {
  7401. _canvas.width = destWidth;
  7402. _canvas.height = destHeight;
  7403. }
  7404. // rotate if required, according to orientation tag
  7405. if (!_preserveHeaders) {
  7406. _rotateToOrientaion(_canvas.width, _canvas.height, orientation);
  7407. }
  7408. _drawToCanvas.call(this, img, _canvas, -x, -y, destWidth, destHeight);
  7409. this.width = _canvas.width;
  7410. this.height = _canvas.height;
  7411. _modified = true;
  7412. self.trigger('Resize');
  7413. }
  7414. function _drawToCanvas(img, canvas, x, y, w, h) {
  7415. if (Env.OS === 'iOS') {
  7416. // avoid squish bug in iOS6
  7417. MegaPixel.renderTo(img, canvas, { width: w, height: h, x: x, y: y });
  7418. } else {
  7419. var ctx = canvas.getContext('2d');
  7420. ctx.drawImage(img, x, y, w, h);
  7421. }
  7422. }
  7423. /**
  7424. * Transform canvas coordination according to specified frame size and orientation
  7425. * Orientation value is from EXIF tag
  7426. * @author Shinichi Tomita <shinichi.tomita@gmail.com>
  7427. */
  7428. function _rotateToOrientaion(width, height, orientation) {
  7429. switch (orientation) {
  7430. case 5:
  7431. case 6:
  7432. case 7:
  7433. case 8:
  7434. _canvas.width = height;
  7435. _canvas.height = width;
  7436. break;
  7437. default:
  7438. _canvas.width = width;
  7439. _canvas.height = height;
  7440. }
  7441. /**
  7442. 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
  7443. 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
  7444. 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
  7445. 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
  7446. 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
  7447. 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
  7448. 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
  7449. 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
  7450. */
  7451. var ctx = _canvas.getContext('2d');
  7452. switch (orientation) {
  7453. case 2:
  7454. // horizontal flip
  7455. ctx.translate(width, 0);
  7456. ctx.scale(-1, 1);
  7457. break;
  7458. case 3:
  7459. // 180 rotate left
  7460. ctx.translate(width, height);
  7461. ctx.rotate(Math.PI);
  7462. break;
  7463. case 4:
  7464. // vertical flip
  7465. ctx.translate(0, height);
  7466. ctx.scale(1, -1);
  7467. break;
  7468. case 5:
  7469. // vertical flip + 90 rotate right
  7470. ctx.rotate(0.5 * Math.PI);
  7471. ctx.scale(1, -1);
  7472. break;
  7473. case 6:
  7474. // 90 rotate right
  7475. ctx.rotate(0.5 * Math.PI);
  7476. ctx.translate(0, -height);
  7477. break;
  7478. case 7:
  7479. // horizontal flip + 90 rotate right
  7480. ctx.rotate(0.5 * Math.PI);
  7481. ctx.translate(width, -height);
  7482. ctx.scale(-1, 1);
  7483. break;
  7484. case 8:
  7485. // 90 rotate left
  7486. ctx.rotate(-0.5 * Math.PI);
  7487. ctx.translate(-width, 0);
  7488. break;
  7489. }
  7490. }
  7491. function _purge() {
  7492. if (_imgInfo) {
  7493. _imgInfo.purge();
  7494. _imgInfo = null;
  7495. }
  7496. _binStr = _img = _canvas = _blob = null;
  7497. _modified = false;
  7498. }
  7499. }
  7500. return (extensions.Image = HTML5Image);
  7501. });
  7502. // Included from: src/javascript/runtime/flash/Runtime.js
  7503. /**
  7504. * Runtime.js
  7505. *
  7506. * Copyright 2013, Moxiecode Systems AB
  7507. * Released under GPL License.
  7508. *
  7509. * License: http://www.plupload.com/license
  7510. * Contributing: http://www.plupload.com/contributing
  7511. */
  7512. /*global ActiveXObject:true */
  7513. /**
  7514. Defines constructor for Flash runtime.
  7515. @class moxie/runtime/flash/Runtime
  7516. @private
  7517. */
  7518. define("moxie/runtime/flash/Runtime", [
  7519. "moxie/core/utils/Basic",
  7520. "moxie/core/utils/Env",
  7521. "moxie/core/utils/Dom",
  7522. "moxie/core/Exceptions",
  7523. "moxie/runtime/Runtime"
  7524. ], function(Basic, Env, Dom, x, Runtime) {
  7525. var type = 'flash', extensions = {};
  7526. /**
  7527. Get the version of the Flash Player
  7528. @method getShimVersion
  7529. @private
  7530. @return {Number} Flash Player version
  7531. */
  7532. function getShimVersion() {
  7533. var version;
  7534. try {
  7535. version = navigator.plugins['Shockwave Flash'];
  7536. version = version.description;
  7537. } catch (e1) {
  7538. try {
  7539. version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
  7540. } catch (e2) {
  7541. version = '0.0';
  7542. }
  7543. }
  7544. version = version.match(/\d+/g);
  7545. return parseFloat(version[0] + '.' + version[1]);
  7546. }
  7547. /**
  7548. Cross-browser SWF removal
  7549. - Especially needed to safely and completely remove a SWF in Internet Explorer
  7550. Originated from SWFObject v2.2 <http://code.google.com/p/swfobject/>
  7551. */
  7552. function removeSWF(id) {
  7553. var obj = Dom.get(id);
  7554. if (obj && obj.nodeName == "OBJECT") {
  7555. if (Env.browser === 'IE') {
  7556. obj.style.display = "none";
  7557. (function onInit(){
  7558. // http://msdn.microsoft.com/en-us/library/ie/ms534360(v=vs.85).aspx
  7559. if (obj.readyState == 4) {
  7560. removeObjectInIE(id);
  7561. }
  7562. else {
  7563. setTimeout(onInit, 10);
  7564. }
  7565. })();
  7566. }
  7567. else {
  7568. obj.parentNode.removeChild(obj);
  7569. }
  7570. }
  7571. }
  7572. function removeObjectInIE(id) {
  7573. var obj = Dom.get(id);
  7574. if (obj) {
  7575. for (var i in obj) {
  7576. if (typeof obj[i] == "function") {
  7577. obj[i] = null;
  7578. }
  7579. }
  7580. obj.parentNode.removeChild(obj);
  7581. }
  7582. }
  7583. /**
  7584. Constructor for the Flash Runtime
  7585. @class FlashRuntime
  7586. @extends Runtime
  7587. */
  7588. function FlashRuntime(options) {
  7589. var I = this, initTimer;
  7590. options = Basic.extend({ swf_url: Env.swf_url }, options);
  7591. Runtime.call(this, options, type, {
  7592. access_binary: function(value) {
  7593. return value && I.mode === 'browser';
  7594. },
  7595. access_image_binary: function(value) {
  7596. return value && I.mode === 'browser';
  7597. },
  7598. display_media: Runtime.capTrue,
  7599. do_cors: Runtime.capTrue,
  7600. drag_and_drop: false,
  7601. report_upload_progress: function() {
  7602. return I.mode === 'client';
  7603. },
  7604. resize_image: Runtime.capTrue,
  7605. return_response_headers: false,
  7606. return_response_type: function(responseType) {
  7607. if (responseType === 'json' && !!window.JSON) {
  7608. return true;
  7609. }
  7610. return !Basic.arrayDiff(responseType, ['', 'text', 'document']) || I.mode === 'browser';
  7611. },
  7612. return_status_code: function(code) {
  7613. return I.mode === 'browser' || !Basic.arrayDiff(code, [200, 404]);
  7614. },
  7615. select_file: Runtime.capTrue,
  7616. select_multiple: Runtime.capTrue,
  7617. send_binary_string: function(value) {
  7618. return value && I.mode === 'browser';
  7619. },
  7620. send_browser_cookies: function(value) {
  7621. return value && I.mode === 'browser';
  7622. },
  7623. send_custom_headers: function(value) {
  7624. return value && I.mode === 'browser';
  7625. },
  7626. send_multipart: Runtime.capTrue,
  7627. slice_blob: function(value) {
  7628. return value && I.mode === 'browser';
  7629. },
  7630. stream_upload: function(value) {
  7631. return value && I.mode === 'browser';
  7632. },
  7633. summon_file_dialog: false,
  7634. upload_filesize: function(size) {
  7635. return Basic.parseSizeStr(size) <= 2097152 || I.mode === 'client';
  7636. },
  7637. use_http_method: function(methods) {
  7638. return !Basic.arrayDiff(methods, ['GET', 'POST']);
  7639. }
  7640. }, {
  7641. // capabilities that require specific mode
  7642. access_binary: function(value) {
  7643. return value ? 'browser' : 'client';
  7644. },
  7645. access_image_binary: function(value) {
  7646. return value ? 'browser' : 'client';
  7647. },
  7648. report_upload_progress: function(value) {
  7649. return value ? 'browser' : 'client';
  7650. },
  7651. return_response_type: function(responseType) {
  7652. return Basic.arrayDiff(responseType, ['', 'text', 'json', 'document']) ? 'browser' : ['client', 'browser'];
  7653. },
  7654. return_status_code: function(code) {
  7655. return Basic.arrayDiff(code, [200, 404]) ? 'browser' : ['client', 'browser'];
  7656. },
  7657. send_binary_string: function(value) {
  7658. return value ? 'browser' : 'client';
  7659. },
  7660. send_browser_cookies: function(value) {
  7661. return value ? 'browser' : 'client';
  7662. },
  7663. send_custom_headers: function(value) {
  7664. return value ? 'browser' : 'client';
  7665. },
  7666. stream_upload: function(value) {
  7667. return value ? 'client' : 'browser';
  7668. },
  7669. upload_filesize: function(size) {
  7670. return Basic.parseSizeStr(size) >= 2097152 ? 'client' : 'browser';
  7671. }
  7672. }, 'client');
  7673. // minimal requirement for Flash Player version
  7674. if (getShimVersion() < 10) {
  7675. if (MXI_DEBUG && Env.debug.runtime) {
  7676. Env.log("\tFlash didn't meet minimal version requirement (10).");
  7677. }
  7678. this.mode = false; // with falsy mode, runtime won't operable, no matter what the mode was before
  7679. }
  7680. Basic.extend(this, {
  7681. getShim: function() {
  7682. return Dom.get(this.uid);
  7683. },
  7684. shimExec: function(component, action) {
  7685. var args = [].slice.call(arguments, 2);
  7686. return I.getShim().exec(this.uid, component, action, args);
  7687. },
  7688. init: function() {
  7689. var html, el, container;
  7690. container = this.getShimContainer();
  7691. // if not the minimal height, shims are not initialized in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)
  7692. Basic.extend(container.style, {
  7693. position: 'absolute',
  7694. top: '-8px',
  7695. left: '-8px',
  7696. width: '9px',
  7697. height: '9px',
  7698. overflow: 'hidden'
  7699. });
  7700. // insert flash object
  7701. html = '<object id="' + this.uid + '" type="application/x-shockwave-flash" data="' + options.swf_url + '" ';
  7702. if (Env.browser === 'IE') {
  7703. html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
  7704. }
  7705. html += 'width="100%" height="100%" style="outline:0">' +
  7706. '<param name="movie" value="' + options.swf_url + '" />' +
  7707. '<param name="flashvars" value="uid=' + escape(this.uid) + '&target=' + Env.global_event_dispatcher + '" />' +
  7708. '<param name="wmode" value="transparent" />' +
  7709. '<param name="allowscriptaccess" value="always" />' +
  7710. '</object>';
  7711. if (Env.browser === 'IE') {
  7712. el = document.createElement('div');
  7713. container.appendChild(el);
  7714. el.outerHTML = html;
  7715. el = container = null; // just in case
  7716. } else {
  7717. container.innerHTML = html;
  7718. }
  7719. // Init is dispatched by the shim
  7720. initTimer = setTimeout(function() {
  7721. if (I && !I.initialized) { // runtime might be already destroyed by this moment
  7722. I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  7723. if (MXI_DEBUG && Env.debug.runtime) {
  7724. Env.log("\tFlash failed to initialize within a specified period of time (typically 5s).");
  7725. }
  7726. }
  7727. }, 5000);
  7728. },
  7729. destroy: (function(destroy) { // extend default destroy method
  7730. return function() {
  7731. removeSWF(I.uid); // SWF removal requires special care in IE
  7732. destroy.call(I);
  7733. clearTimeout(initTimer); // initialization check might be still onwait
  7734. options = initTimer = destroy = I = null;
  7735. };
  7736. }(this.destroy))
  7737. }, extensions);
  7738. }
  7739. Runtime.addConstructor(type, FlashRuntime);
  7740. return extensions;
  7741. });
  7742. // Included from: src/javascript/runtime/flash/file/FileInput.js
  7743. /**
  7744. * FileInput.js
  7745. *
  7746. * Copyright 2013, Moxiecode Systems AB
  7747. * Released under GPL License.
  7748. *
  7749. * License: http://www.plupload.com/license
  7750. * Contributing: http://www.plupload.com/contributing
  7751. */
  7752. /**
  7753. @class moxie/runtime/flash/file/FileInput
  7754. @private
  7755. */
  7756. define("moxie/runtime/flash/file/FileInput", [
  7757. "moxie/runtime/flash/Runtime",
  7758. "moxie/file/File",
  7759. "moxie/core/utils/Basic"
  7760. ], function(extensions, File, Basic) {
  7761. var FileInput = {
  7762. init: function(options) {
  7763. var comp = this, I = this.getRuntime();
  7764. this.bind("Change", function() {
  7765. var files = I.shimExec.call(comp, 'FileInput', 'getFiles');
  7766. comp.files = [];
  7767. Basic.each(files, function(file) {
  7768. comp.files.push(new File(I.uid, file));
  7769. });
  7770. }, 999);
  7771. this.getRuntime().shimExec.call(this, 'FileInput', 'init', {
  7772. name: options.name,
  7773. accept: options.accept,
  7774. multiple: options.multiple
  7775. });
  7776. this.trigger('ready');
  7777. }
  7778. };
  7779. return (extensions.FileInput = FileInput);
  7780. });
  7781. // Included from: src/javascript/runtime/flash/file/Blob.js
  7782. /**
  7783. * Blob.js
  7784. *
  7785. * Copyright 2013, Moxiecode Systems AB
  7786. * Released under GPL License.
  7787. *
  7788. * License: http://www.plupload.com/license
  7789. * Contributing: http://www.plupload.com/contributing
  7790. */
  7791. /**
  7792. @class moxie/runtime/flash/file/Blob
  7793. @private
  7794. */
  7795. define("moxie/runtime/flash/file/Blob", [
  7796. "moxie/runtime/flash/Runtime",
  7797. "moxie/file/Blob"
  7798. ], function(extensions, Blob) {
  7799. var FlashBlob = {
  7800. slice: function(blob, start, end, type) {
  7801. var self = this.getRuntime();
  7802. if (start < 0) {
  7803. start = Math.max(blob.size + start, 0);
  7804. } else if (start > 0) {
  7805. start = Math.min(start, blob.size);
  7806. }
  7807. if (end < 0) {
  7808. end = Math.max(blob.size + end, 0);
  7809. } else if (end > 0) {
  7810. end = Math.min(end, blob.size);
  7811. }
  7812. blob = self.shimExec.call(this, 'Blob', 'slice', start, end, type || '');
  7813. if (blob) {
  7814. blob = new Blob(self.uid, blob);
  7815. }
  7816. return blob;
  7817. }
  7818. };
  7819. return (extensions.Blob = FlashBlob);
  7820. });
  7821. // Included from: src/javascript/runtime/flash/file/FileReader.js
  7822. /**
  7823. * FileReader.js
  7824. *
  7825. * Copyright 2013, Moxiecode Systems AB
  7826. * Released under GPL License.
  7827. *
  7828. * License: http://www.plupload.com/license
  7829. * Contributing: http://www.plupload.com/contributing
  7830. */
  7831. /**
  7832. @class moxie/runtime/flash/file/FileReader
  7833. @private
  7834. */
  7835. define("moxie/runtime/flash/file/FileReader", [
  7836. "moxie/runtime/flash/Runtime",
  7837. "moxie/core/utils/Encode"
  7838. ], function(extensions, Encode) {
  7839. function _formatData(data, op) {
  7840. switch (op) {
  7841. case 'readAsText':
  7842. return Encode.atob(data, 'utf8');
  7843. case 'readAsBinaryString':
  7844. return Encode.atob(data);
  7845. case 'readAsDataURL':
  7846. return data;
  7847. }
  7848. return null;
  7849. }
  7850. var FileReader = {
  7851. read: function(op, blob) {
  7852. var comp = this;
  7853. comp.result = '';
  7854. // special prefix for DataURL read mode
  7855. if (op === 'readAsDataURL') {
  7856. comp.result = 'data:' + (blob.type || '') + ';base64,';
  7857. }
  7858. comp.bind('Progress', function(e, data) {
  7859. if (data) {
  7860. comp.result += _formatData(data, op);
  7861. }
  7862. }, 999);
  7863. return comp.getRuntime().shimExec.call(this, 'FileReader', 'readAsBase64', blob.uid);
  7864. }
  7865. };
  7866. return (extensions.FileReader = FileReader);
  7867. });
  7868. // Included from: src/javascript/runtime/flash/file/FileReaderSync.js
  7869. /**
  7870. * FileReaderSync.js
  7871. *
  7872. * Copyright 2013, Moxiecode Systems AB
  7873. * Released under GPL License.
  7874. *
  7875. * License: http://www.plupload.com/license
  7876. * Contributing: http://www.plupload.com/contributing
  7877. */
  7878. /**
  7879. @class moxie/runtime/flash/file/FileReaderSync
  7880. @private
  7881. */
  7882. define("moxie/runtime/flash/file/FileReaderSync", [
  7883. "moxie/runtime/flash/Runtime",
  7884. "moxie/core/utils/Encode"
  7885. ], function(extensions, Encode) {
  7886. function _formatData(data, op) {
  7887. switch (op) {
  7888. case 'readAsText':
  7889. return Encode.atob(data, 'utf8');
  7890. case 'readAsBinaryString':
  7891. return Encode.atob(data);
  7892. case 'readAsDataURL':
  7893. return data;
  7894. }
  7895. return null;
  7896. }
  7897. var FileReaderSync = {
  7898. read: function(op, blob) {
  7899. var result, self = this.getRuntime();
  7900. result = self.shimExec.call(this, 'FileReaderSync', 'readAsBase64', blob.uid);
  7901. if (!result) {
  7902. return null; // or throw ex
  7903. }
  7904. // special prefix for DataURL read mode
  7905. if (op === 'readAsDataURL') {
  7906. result = 'data:' + (blob.type || '') + ';base64,' + result;
  7907. }
  7908. return _formatData(result, op, blob.type);
  7909. }
  7910. };
  7911. return (extensions.FileReaderSync = FileReaderSync);
  7912. });
  7913. // Included from: src/javascript/runtime/flash/xhr/XMLHttpRequest.js
  7914. /**
  7915. * XMLHttpRequest.js
  7916. *
  7917. * Copyright 2013, Moxiecode Systems AB
  7918. * Released under GPL License.
  7919. *
  7920. * License: http://www.plupload.com/license
  7921. * Contributing: http://www.plupload.com/contributing
  7922. */
  7923. /**
  7924. @class moxie/runtime/flash/xhr/XMLHttpRequest
  7925. @private
  7926. */
  7927. define("moxie/runtime/flash/xhr/XMLHttpRequest", [
  7928. "moxie/runtime/flash/Runtime",
  7929. "moxie/core/utils/Basic",
  7930. "moxie/file/Blob",
  7931. "moxie/file/File",
  7932. "moxie/file/FileReaderSync",
  7933. "moxie/xhr/FormData",
  7934. "moxie/runtime/Transporter"
  7935. ], function(extensions, Basic, Blob, File, FileReaderSync, FormData, Transporter) {
  7936. var XMLHttpRequest = {
  7937. send: function(meta, data) {
  7938. var target = this, self = target.getRuntime();
  7939. function send() {
  7940. meta.transport = self.mode;
  7941. self.shimExec.call(target, 'XMLHttpRequest', 'send', meta, data);
  7942. }
  7943. function appendBlob(name, blob) {
  7944. self.shimExec.call(target, 'XMLHttpRequest', 'appendBlob', name, blob.uid);
  7945. data = null;
  7946. send();
  7947. }
  7948. function attachBlob(blob, cb) {
  7949. var tr = new Transporter();
  7950. tr.bind("TransportingComplete", function() {
  7951. cb(this.result);
  7952. });
  7953. tr.transport(blob.getSource(), blob.type, {
  7954. ruid: self.uid
  7955. });
  7956. }
  7957. // copy over the headers if any
  7958. if (!Basic.isEmptyObj(meta.headers)) {
  7959. Basic.each(meta.headers, function(value, header) {
  7960. self.shimExec.call(target, 'XMLHttpRequest', 'setRequestHeader', header, value.toString()); // Silverlight doesn't accept integers into the arguments of type object
  7961. });
  7962. }
  7963. // transfer over multipart params and blob itself
  7964. if (data instanceof FormData) {
  7965. var blobField;
  7966. data.each(function(value, name) {
  7967. if (value instanceof Blob) {
  7968. blobField = name;
  7969. } else {
  7970. self.shimExec.call(target, 'XMLHttpRequest', 'append', name, value);
  7971. }
  7972. });
  7973. if (!data.hasBlob()) {
  7974. data = null;
  7975. send();
  7976. } else {
  7977. var blob = data.getBlob();
  7978. if (blob.isDetached()) {
  7979. attachBlob(blob, function(attachedBlob) {
  7980. blob.destroy();
  7981. appendBlob(blobField, attachedBlob);
  7982. });
  7983. } else {
  7984. appendBlob(blobField, blob);
  7985. }
  7986. }
  7987. } else if (data instanceof Blob) {
  7988. if (data.isDetached()) {
  7989. attachBlob(data, function(attachedBlob) {
  7990. data.destroy();
  7991. data = attachedBlob.uid;
  7992. send();
  7993. });
  7994. } else {
  7995. data = data.uid;
  7996. send();
  7997. }
  7998. } else {
  7999. send();
  8000. }
  8001. },
  8002. getResponse: function(responseType) {
  8003. var frs, blob, self = this.getRuntime();
  8004. blob = self.shimExec.call(this, 'XMLHttpRequest', 'getResponseAsBlob');
  8005. if (blob) {
  8006. blob = new File(self.uid, blob);
  8007. if ('blob' === responseType) {
  8008. return blob;
  8009. }
  8010. try {
  8011. frs = new FileReaderSync();
  8012. if (!!~Basic.inArray(responseType, ["", "text"])) {
  8013. return frs.readAsText(blob);
  8014. } else if ('json' === responseType && !!window.JSON) {
  8015. return JSON.parse(frs.readAsText(blob));
  8016. }
  8017. } finally {
  8018. blob.destroy();
  8019. }
  8020. }
  8021. return null;
  8022. },
  8023. abort: function(upload_complete_flag) {
  8024. var self = this.getRuntime();
  8025. self.shimExec.call(this, 'XMLHttpRequest', 'abort');
  8026. this.dispatchEvent('readystatechange');
  8027. // this.dispatchEvent('progress');
  8028. this.dispatchEvent('abort');
  8029. //if (!upload_complete_flag) {
  8030. // this.dispatchEvent('uploadprogress');
  8031. //}
  8032. }
  8033. };
  8034. return (extensions.XMLHttpRequest = XMLHttpRequest);
  8035. });
  8036. // Included from: src/javascript/runtime/flash/runtime/Transporter.js
  8037. /**
  8038. * Transporter.js
  8039. *
  8040. * Copyright 2013, Moxiecode Systems AB
  8041. * Released under GPL License.
  8042. *
  8043. * License: http://www.plupload.com/license
  8044. * Contributing: http://www.plupload.com/contributing
  8045. */
  8046. /**
  8047. @class moxie/runtime/flash/runtime/Transporter
  8048. @private
  8049. */
  8050. define("moxie/runtime/flash/runtime/Transporter", [
  8051. "moxie/runtime/flash/Runtime",
  8052. "moxie/file/Blob"
  8053. ], function(extensions, Blob) {
  8054. var Transporter = {
  8055. getAsBlob: function(type) {
  8056. var self = this.getRuntime()
  8057. , blob = self.shimExec.call(this, 'Transporter', 'getAsBlob', type)
  8058. ;
  8059. if (blob) {
  8060. return new Blob(self.uid, blob);
  8061. }
  8062. return null;
  8063. }
  8064. };
  8065. return (extensions.Transporter = Transporter);
  8066. });
  8067. // Included from: src/javascript/runtime/flash/image/Image.js
  8068. /**
  8069. * Image.js
  8070. *
  8071. * Copyright 2013, Moxiecode Systems AB
  8072. * Released under GPL License.
  8073. *
  8074. * License: http://www.plupload.com/license
  8075. * Contributing: http://www.plupload.com/contributing
  8076. */
  8077. /**
  8078. @class moxie/runtime/flash/image/Image
  8079. @private
  8080. */
  8081. define("moxie/runtime/flash/image/Image", [
  8082. "moxie/runtime/flash/Runtime",
  8083. "moxie/core/utils/Basic",
  8084. "moxie/runtime/Transporter",
  8085. "moxie/file/Blob",
  8086. "moxie/file/FileReaderSync"
  8087. ], function(extensions, Basic, Transporter, Blob, FileReaderSync) {
  8088. var Image = {
  8089. loadFromBlob: function(blob) {
  8090. var comp = this, self = comp.getRuntime();
  8091. function exec(srcBlob) {
  8092. self.shimExec.call(comp, 'Image', 'loadFromBlob', srcBlob.uid);
  8093. comp = self = null;
  8094. }
  8095. if (blob.isDetached()) { // binary string
  8096. var tr = new Transporter();
  8097. tr.bind("TransportingComplete", function() {
  8098. exec(tr.result.getSource());
  8099. });
  8100. tr.transport(blob.getSource(), blob.type, { ruid: self.uid });
  8101. } else {
  8102. exec(blob.getSource());
  8103. }
  8104. },
  8105. loadFromImage: function(img) {
  8106. var self = this.getRuntime();
  8107. return self.shimExec.call(this, 'Image', 'loadFromImage', img.uid);
  8108. },
  8109. getInfo: function() {
  8110. var self = this.getRuntime()
  8111. , info = self.shimExec.call(this, 'Image', 'getInfo')
  8112. ;
  8113. if (info.meta && info.meta.thumb && !(info.meta.thumb.data instanceof Blob)) {
  8114. info.meta.thumb.data = new Blob(self.uid, info.meta.thumb.data);
  8115. }
  8116. return info;
  8117. },
  8118. getAsBlob: function(type, quality) {
  8119. var self = this.getRuntime()
  8120. , blob = self.shimExec.call(this, 'Image', 'getAsBlob', type, quality)
  8121. ;
  8122. if (blob) {
  8123. return new Blob(self.uid, blob);
  8124. }
  8125. return null;
  8126. },
  8127. getAsDataURL: function() {
  8128. var self = this.getRuntime()
  8129. , blob = self.Image.getAsBlob.apply(this, arguments)
  8130. , frs
  8131. ;
  8132. if (!blob) {
  8133. return null;
  8134. }
  8135. frs = new FileReaderSync();
  8136. return frs.readAsDataURL(blob);
  8137. }
  8138. };
  8139. return (extensions.Image = Image);
  8140. });
  8141. // Included from: src/javascript/runtime/silverlight/Runtime.js
  8142. /**
  8143. * RunTime.js
  8144. *
  8145. * Copyright 2013, Moxiecode Systems AB
  8146. * Released under GPL License.
  8147. *
  8148. * License: http://www.plupload.com/license
  8149. * Contributing: http://www.plupload.com/contributing
  8150. */
  8151. /*global ActiveXObject:true */
  8152. /**
  8153. Defines constructor for Silverlight runtime.
  8154. @class moxie/runtime/silverlight/Runtime
  8155. @private
  8156. */
  8157. define("moxie/runtime/silverlight/Runtime", [
  8158. "moxie/core/utils/Basic",
  8159. "moxie/core/utils/Env",
  8160. "moxie/core/utils/Dom",
  8161. "moxie/core/Exceptions",
  8162. "moxie/runtime/Runtime"
  8163. ], function(Basic, Env, Dom, x, Runtime) {
  8164. var type = "silverlight", extensions = {};
  8165. function isInstalled(version) {
  8166. var isVersionSupported = false, control = null, actualVer,
  8167. actualVerArray, reqVerArray, requiredVersionPart, actualVersionPart, index = 0;
  8168. try {
  8169. try {
  8170. control = new ActiveXObject('AgControl.AgControl');
  8171. if (control.IsVersionSupported(version)) {
  8172. isVersionSupported = true;
  8173. }
  8174. control = null;
  8175. } catch (e) {
  8176. var plugin = navigator.plugins["Silverlight Plug-In"];
  8177. if (plugin) {
  8178. actualVer = plugin.description;
  8179. if (actualVer === "1.0.30226.2") {
  8180. actualVer = "2.0.30226.2";
  8181. }
  8182. actualVerArray = actualVer.split(".");
  8183. while (actualVerArray.length > 3) {
  8184. actualVerArray.pop();
  8185. }
  8186. while ( actualVerArray.length < 4) {
  8187. actualVerArray.push(0);
  8188. }
  8189. reqVerArray = version.split(".");
  8190. while (reqVerArray.length > 4) {
  8191. reqVerArray.pop();
  8192. }
  8193. do {
  8194. requiredVersionPart = parseInt(reqVerArray[index], 10);
  8195. actualVersionPart = parseInt(actualVerArray[index], 10);
  8196. index++;
  8197. } while (index < reqVerArray.length && requiredVersionPart === actualVersionPart);
  8198. if (requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart)) {
  8199. isVersionSupported = true;
  8200. }
  8201. }
  8202. }
  8203. } catch (e2) {
  8204. isVersionSupported = false;
  8205. }
  8206. return isVersionSupported;
  8207. }
  8208. /**
  8209. Constructor for the Silverlight Runtime
  8210. @class SilverlightRuntime
  8211. @extends Runtime
  8212. */
  8213. function SilverlightRuntime(options) {
  8214. var I = this, initTimer;
  8215. options = Basic.extend({ xap_url: Env.xap_url }, options);
  8216. Runtime.call(this, options, type, {
  8217. access_binary: Runtime.capTrue,
  8218. access_image_binary: Runtime.capTrue,
  8219. display_media: Runtime.capTrue,
  8220. do_cors: Runtime.capTrue,
  8221. drag_and_drop: false,
  8222. report_upload_progress: Runtime.capTrue,
  8223. resize_image: Runtime.capTrue,
  8224. return_response_headers: function(value) {
  8225. return value && I.mode === 'client';
  8226. },
  8227. return_response_type: function(responseType) {
  8228. if (responseType !== 'json') {
  8229. return true;
  8230. } else {
  8231. return !!window.JSON;
  8232. }
  8233. },
  8234. return_status_code: function(code) {
  8235. return I.mode === 'client' || !Basic.arrayDiff(code, [200, 404]);
  8236. },
  8237. select_file: Runtime.capTrue,
  8238. select_multiple: Runtime.capTrue,
  8239. send_binary_string: Runtime.capTrue,
  8240. send_browser_cookies: function(value) {
  8241. return value && I.mode === 'browser';
  8242. },
  8243. send_custom_headers: function(value) {
  8244. return value && I.mode === 'client';
  8245. },
  8246. send_multipart: Runtime.capTrue,
  8247. slice_blob: Runtime.capTrue,
  8248. stream_upload: true,
  8249. summon_file_dialog: false,
  8250. upload_filesize: Runtime.capTrue,
  8251. use_http_method: function(methods) {
  8252. return I.mode === 'client' || !Basic.arrayDiff(methods, ['GET', 'POST']);
  8253. }
  8254. }, {
  8255. // capabilities that require specific mode
  8256. return_response_headers: function(value) {
  8257. return value ? 'client' : 'browser';
  8258. },
  8259. return_status_code: function(code) {
  8260. return Basic.arrayDiff(code, [200, 404]) ? 'client' : ['client', 'browser'];
  8261. },
  8262. send_browser_cookies: function(value) {
  8263. return value ? 'browser' : 'client';
  8264. },
  8265. send_custom_headers: function(value) {
  8266. return value ? 'client' : 'browser';
  8267. },
  8268. use_http_method: function(methods) {
  8269. return Basic.arrayDiff(methods, ['GET', 'POST']) ? 'client' : ['client', 'browser'];
  8270. }
  8271. });
  8272. // minimal requirement
  8273. if (!isInstalled('2.0.31005.0') || Env.browser === 'Opera') {
  8274. if (MXI_DEBUG && Env.debug.runtime) {
  8275. Env.log("\tSilverlight is not installed or minimal version (2.0.31005.0) requirement not met (not likely).");
  8276. }
  8277. this.mode = false;
  8278. }
  8279. Basic.extend(this, {
  8280. getShim: function() {
  8281. return Dom.get(this.uid).content.Moxie;
  8282. },
  8283. shimExec: function(component, action) {
  8284. var args = [].slice.call(arguments, 2);
  8285. return I.getShim().exec(this.uid, component, action, args);
  8286. },
  8287. init : function() {
  8288. var container;
  8289. container = this.getShimContainer();
  8290. container.innerHTML = '<object id="' + this.uid + '" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">' +
  8291. '<param name="source" value="' + options.xap_url + '"/>' +
  8292. '<param name="background" value="Transparent"/>' +
  8293. '<param name="windowless" value="true"/>' +
  8294. '<param name="enablehtmlaccess" value="true"/>' +
  8295. '<param name="initParams" value="uid=' + this.uid + ',target=' + Env.global_event_dispatcher + '"/>' +
  8296. '</object>';
  8297. // Init is dispatched by the shim
  8298. initTimer = setTimeout(function() {
  8299. if (I && !I.initialized) { // runtime might be already destroyed by this moment
  8300. I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
  8301. if (MXI_DEBUG && Env.debug.runtime) {
  8302. Env.log("\Silverlight failed to initialize within a specified period of time (5-10s).");
  8303. }
  8304. }
  8305. }, Env.OS !== 'Windows'? 10000 : 5000); // give it more time to initialize in non Windows OS (like Mac)
  8306. },
  8307. destroy: (function(destroy) { // extend default destroy method
  8308. return function() {
  8309. destroy.call(I);
  8310. clearTimeout(initTimer); // initialization check might be still onwait
  8311. options = initTimer = destroy = I = null;
  8312. };
  8313. }(this.destroy))
  8314. }, extensions);
  8315. }
  8316. Runtime.addConstructor(type, SilverlightRuntime);
  8317. return extensions;
  8318. });
  8319. // Included from: src/javascript/runtime/silverlight/file/FileInput.js
  8320. /**
  8321. * FileInput.js
  8322. *
  8323. * Copyright 2013, Moxiecode Systems AB
  8324. * Released under GPL License.
  8325. *
  8326. * License: http://www.plupload.com/license
  8327. * Contributing: http://www.plupload.com/contributing
  8328. */
  8329. /**
  8330. @class moxie/runtime/silverlight/file/FileInput
  8331. @private
  8332. */
  8333. define("moxie/runtime/silverlight/file/FileInput", [
  8334. "moxie/runtime/silverlight/Runtime",
  8335. "moxie/file/File",
  8336. "moxie/core/utils/Basic"
  8337. ], function(extensions, File, Basic) {
  8338. var FileInput = {
  8339. init: function(options) {
  8340. var comp = this, I = this.getRuntime();
  8341. function toFilters(accept) {
  8342. var filter = '';
  8343. for (var i = 0; i < accept.length; i++) {
  8344. filter += (filter !== '' ? '|' : '') + accept[i].title + " | *." + accept[i].extensions.replace(/,/g, ';*.');
  8345. }
  8346. return filter;
  8347. }
  8348. this.bind("Change", function() {
  8349. var files = I.shimExec.call(comp, 'FileInput', 'getFiles');
  8350. comp.files = [];
  8351. Basic.each(files, function(file) {
  8352. comp.files.push(new File(I.uid, file));
  8353. });
  8354. }, 999);
  8355. this.getRuntime().shimExec.call(this, 'FileInput', 'init', toFilters(options.accept), options.name, options.multiple);
  8356. this.trigger('ready');
  8357. }
  8358. };
  8359. return (extensions.FileInput = FileInput);
  8360. });
  8361. // Included from: src/javascript/runtime/silverlight/file/Blob.js
  8362. /**
  8363. * Blob.js
  8364. *
  8365. * Copyright 2013, Moxiecode Systems AB
  8366. * Released under GPL License.
  8367. *
  8368. * License: http://www.plupload.com/license
  8369. * Contributing: http://www.plupload.com/contributing
  8370. */
  8371. /**
  8372. @class moxie/runtime/silverlight/file/Blob
  8373. @private
  8374. */
  8375. define("moxie/runtime/silverlight/file/Blob", [
  8376. "moxie/runtime/silverlight/Runtime",
  8377. "moxie/core/utils/Basic",
  8378. "moxie/runtime/flash/file/Blob"
  8379. ], function(extensions, Basic, Blob) {
  8380. return (extensions.Blob = Basic.extend({}, Blob));
  8381. });
  8382. // Included from: src/javascript/runtime/silverlight/file/FileDrop.js
  8383. /**
  8384. * FileDrop.js
  8385. *
  8386. * Copyright 2013, Moxiecode Systems AB
  8387. * Released under GPL License.
  8388. *
  8389. * License: http://www.plupload.com/license
  8390. * Contributing: http://www.plupload.com/contributing
  8391. */
  8392. /**
  8393. @class moxie/runtime/silverlight/file/FileDrop
  8394. @private
  8395. */
  8396. define("moxie/runtime/silverlight/file/FileDrop", [
  8397. "moxie/runtime/silverlight/Runtime",
  8398. "moxie/core/utils/Dom",
  8399. "moxie/core/utils/Events"
  8400. ], function(extensions, Dom, Events) {
  8401. // not exactly useful, since works only in safari (...crickets...)
  8402. var FileDrop = {
  8403. init: function() {
  8404. var comp = this, self = comp.getRuntime(), dropZone;
  8405. dropZone = self.getShimContainer();
  8406. Events.addEvent(dropZone, 'dragover', function(e) {
  8407. e.preventDefault();
  8408. e.stopPropagation();
  8409. e.dataTransfer.dropEffect = 'copy';
  8410. }, comp.uid);
  8411. Events.addEvent(dropZone, 'dragenter', function(e) {
  8412. e.preventDefault();
  8413. var flag = Dom.get(self.uid).dragEnter(e);
  8414. // If handled, then stop propagation of event in DOM
  8415. if (flag) {
  8416. e.stopPropagation();
  8417. }
  8418. }, comp.uid);
  8419. Events.addEvent(dropZone, 'drop', function(e) {
  8420. e.preventDefault();
  8421. var flag = Dom.get(self.uid).dragDrop(e);
  8422. // If handled, then stop propagation of event in DOM
  8423. if (flag) {
  8424. e.stopPropagation();
  8425. }
  8426. }, comp.uid);
  8427. return self.shimExec.call(this, 'FileDrop', 'init');
  8428. }
  8429. };
  8430. return (extensions.FileDrop = FileDrop);
  8431. });
  8432. // Included from: src/javascript/runtime/silverlight/file/FileReader.js
  8433. /**
  8434. * FileReader.js
  8435. *
  8436. * Copyright 2013, Moxiecode Systems AB
  8437. * Released under GPL License.
  8438. *
  8439. * License: http://www.plupload.com/license
  8440. * Contributing: http://www.plupload.com/contributing
  8441. */
  8442. /**
  8443. @class moxie/runtime/silverlight/file/FileReader
  8444. @private
  8445. */
  8446. define("moxie/runtime/silverlight/file/FileReader", [
  8447. "moxie/runtime/silverlight/Runtime",
  8448. "moxie/core/utils/Basic",
  8449. "moxie/runtime/flash/file/FileReader"
  8450. ], function(extensions, Basic, FileReader) {
  8451. return (extensions.FileReader = Basic.extend({}, FileReader));
  8452. });
  8453. // Included from: src/javascript/runtime/silverlight/file/FileReaderSync.js
  8454. /**
  8455. * FileReaderSync.js
  8456. *
  8457. * Copyright 2013, Moxiecode Systems AB
  8458. * Released under GPL License.
  8459. *
  8460. * License: http://www.plupload.com/license
  8461. * Contributing: http://www.plupload.com/contributing
  8462. */
  8463. /**
  8464. @class moxie/runtime/silverlight/file/FileReaderSync
  8465. @private
  8466. */
  8467. define("moxie/runtime/silverlight/file/FileReaderSync", [
  8468. "moxie/runtime/silverlight/Runtime",
  8469. "moxie/core/utils/Basic",
  8470. "moxie/runtime/flash/file/FileReaderSync"
  8471. ], function(extensions, Basic, FileReaderSync) {
  8472. return (extensions.FileReaderSync = Basic.extend({}, FileReaderSync));
  8473. });
  8474. // Included from: src/javascript/runtime/silverlight/xhr/XMLHttpRequest.js
  8475. /**
  8476. * XMLHttpRequest.js
  8477. *
  8478. * Copyright 2013, Moxiecode Systems AB
  8479. * Released under GPL License.
  8480. *
  8481. * License: http://www.plupload.com/license
  8482. * Contributing: http://www.plupload.com/contributing
  8483. */
  8484. /**
  8485. @class moxie/runtime/silverlight/xhr/XMLHttpRequest
  8486. @private
  8487. */
  8488. define("moxie/runtime/silverlight/xhr/XMLHttpRequest", [
  8489. "moxie/runtime/silverlight/Runtime",
  8490. "moxie/core/utils/Basic",
  8491. "moxie/runtime/flash/xhr/XMLHttpRequest"
  8492. ], function(extensions, Basic, XMLHttpRequest) {
  8493. return (extensions.XMLHttpRequest = Basic.extend({}, XMLHttpRequest));
  8494. });
  8495. // Included from: src/javascript/runtime/silverlight/runtime/Transporter.js
  8496. /**
  8497. * Transporter.js
  8498. *
  8499. * Copyright 2013, Moxiecode Systems AB
  8500. * Released under GPL License.
  8501. *
  8502. * License: http://www.plupload.com/license
  8503. * Contributing: http://www.plupload.com/contributing
  8504. */
  8505. /**
  8506. @class moxie/runtime/silverlight/runtime/Transporter
  8507. @private
  8508. */
  8509. define("moxie/runtime/silverlight/runtime/Transporter", [
  8510. "moxie/runtime/silverlight/Runtime",
  8511. "moxie/core/utils/Basic",
  8512. "moxie/runtime/flash/runtime/Transporter"
  8513. ], function(extensions, Basic, Transporter) {
  8514. return (extensions.Transporter = Basic.extend({}, Transporter));
  8515. });
  8516. // Included from: src/javascript/runtime/silverlight/image/Image.js
  8517. /**
  8518. * Image.js
  8519. *
  8520. * Copyright 2013, Moxiecode Systems AB
  8521. * Released under GPL License.
  8522. *
  8523. * License: http://www.plupload.com/license
  8524. * Contributing: http://www.plupload.com/contributing
  8525. */
  8526. /**
  8527. @class moxie/runtime/silverlight/image/Image
  8528. @private
  8529. */
  8530. define("moxie/runtime/silverlight/image/Image", [
  8531. "moxie/runtime/silverlight/Runtime",
  8532. "moxie/core/utils/Basic",
  8533. "moxie/file/Blob",
  8534. "moxie/runtime/flash/image/Image"
  8535. ], function(extensions, Basic, Blob, Image) {
  8536. return (extensions.Image = Basic.extend({}, Image, {
  8537. getInfo: function() {
  8538. var self = this.getRuntime()
  8539. , grps = ['tiff', 'exif', 'gps', 'thumb']
  8540. , info = { meta: {} }
  8541. , rawInfo = self.shimExec.call(this, 'Image', 'getInfo')
  8542. ;
  8543. if (rawInfo.meta) {
  8544. Basic.each(grps, function(grp) {
  8545. var meta = rawInfo.meta[grp]
  8546. , tag
  8547. , i
  8548. , length
  8549. , value
  8550. ;
  8551. if (meta && meta.keys) {
  8552. info.meta[grp] = {};
  8553. for (i = 0, length = meta.keys.length; i < length; i++) {
  8554. tag = meta.keys[i];
  8555. value = meta[tag];
  8556. if (value) {
  8557. // convert numbers
  8558. if (/^(\d|[1-9]\d+)$/.test(value)) { // integer (make sure doesn't start with zero)
  8559. value = parseInt(value, 10);
  8560. } else if (/^\d*\.\d+$/.test(value)) { // double
  8561. value = parseFloat(value);
  8562. }
  8563. info.meta[grp][tag] = value;
  8564. }
  8565. }
  8566. }
  8567. });
  8568. // save thumb data as blob
  8569. if (info.meta && info.meta.thumb && !(info.meta.thumb.data instanceof Blob)) {
  8570. info.meta.thumb.data = new Blob(self.uid, info.meta.thumb.data);
  8571. }
  8572. }
  8573. info.width = parseInt(rawInfo.width, 10);
  8574. info.height = parseInt(rawInfo.height, 10);
  8575. info.size = parseInt(rawInfo.size, 10);
  8576. info.type = rawInfo.type;
  8577. info.name = rawInfo.name;
  8578. return info;
  8579. }
  8580. }));
  8581. });
  8582. // Included from: src/javascript/runtime/html4/Runtime.js
  8583. /**
  8584. * Runtime.js
  8585. *
  8586. * Copyright 2013, Moxiecode Systems AB
  8587. * Released under GPL License.
  8588. *
  8589. * License: http://www.plupload.com/license
  8590. * Contributing: http://www.plupload.com/contributing
  8591. */
  8592. /*global File:true */
  8593. /**
  8594. Defines constructor for HTML4 runtime.
  8595. @class moxie/runtime/html4/Runtime
  8596. @private
  8597. */
  8598. define("moxie/runtime/html4/Runtime", [
  8599. "moxie/core/utils/Basic",
  8600. "moxie/core/Exceptions",
  8601. "moxie/runtime/Runtime",
  8602. "moxie/core/utils/Env"
  8603. ], function(Basic, x, Runtime, Env) {
  8604. var type = 'html4', extensions = {};
  8605. function Html4Runtime(options) {
  8606. var I = this
  8607. , Test = Runtime.capTest
  8608. , True = Runtime.capTrue
  8609. ;
  8610. Runtime.call(this, options, type, {
  8611. access_binary: Test(window.FileReader || window.File && File.getAsDataURL),
  8612. access_image_binary: false,
  8613. display_media: Test(extensions.Image && (Env.can('create_canvas') || Env.can('use_data_uri_over32kb'))),
  8614. do_cors: false,
  8615. drag_and_drop: false,
  8616. filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
  8617. return (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '>=')) ||
  8618. (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
  8619. (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '>='));
  8620. }()),
  8621. resize_image: function() {
  8622. return extensions.Image && I.can('access_binary') && Env.can('create_canvas');
  8623. },
  8624. report_upload_progress: false,
  8625. return_response_headers: false,
  8626. return_response_type: function(responseType) {
  8627. if (responseType === 'json' && !!window.JSON) {
  8628. return true;
  8629. }
  8630. return !!~Basic.inArray(responseType, ['text', 'document', '']);
  8631. },
  8632. return_status_code: function(code) {
  8633. return !Basic.arrayDiff(code, [200, 404]);
  8634. },
  8635. select_file: function() {
  8636. return Env.can('use_fileinput');
  8637. },
  8638. select_multiple: false,
  8639. send_binary_string: false,
  8640. send_custom_headers: false,
  8641. send_multipart: true,
  8642. slice_blob: false,
  8643. stream_upload: function() {
  8644. return I.can('select_file');
  8645. },
  8646. summon_file_dialog: function() { // yeah... some dirty sniffing here...
  8647. return I.can('select_file') && (
  8648. (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '>=')) ||
  8649. (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '>=')) ||
  8650. (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
  8651. !!~Basic.inArray(Env.browser, ['Chrome', 'Safari'])
  8652. );
  8653. },
  8654. upload_filesize: True,
  8655. use_http_method: function(methods) {
  8656. return !Basic.arrayDiff(methods, ['GET', 'POST']);
  8657. }
  8658. });
  8659. Basic.extend(this, {
  8660. init : function() {
  8661. this.trigger("Init");
  8662. },
  8663. destroy: (function(destroy) { // extend default destroy method
  8664. return function() {
  8665. destroy.call(I);
  8666. destroy = I = null;
  8667. };
  8668. }(this.destroy))
  8669. });
  8670. Basic.extend(this.getShim(), extensions);
  8671. }
  8672. Runtime.addConstructor(type, Html4Runtime);
  8673. return extensions;
  8674. });
  8675. // Included from: src/javascript/runtime/html4/file/FileInput.js
  8676. /**
  8677. * FileInput.js
  8678. *
  8679. * Copyright 2013, Moxiecode Systems AB
  8680. * Released under GPL License.
  8681. *
  8682. * License: http://www.plupload.com/license
  8683. * Contributing: http://www.plupload.com/contributing
  8684. */
  8685. /**
  8686. @class moxie/runtime/html4/file/FileInput
  8687. @private
  8688. */
  8689. define("moxie/runtime/html4/file/FileInput", [
  8690. "moxie/runtime/html4/Runtime",
  8691. "moxie/file/File",
  8692. "moxie/core/utils/Basic",
  8693. "moxie/core/utils/Dom",
  8694. "moxie/core/utils/Events",
  8695. "moxie/core/utils/Mime",
  8696. "moxie/core/utils/Env"
  8697. ], function(extensions, File, Basic, Dom, Events, Mime, Env) {
  8698. function FileInput() {
  8699. var _uid, _mimes = [], _options;
  8700. function addInput() {
  8701. var comp = this, I = comp.getRuntime(), shimContainer, browseButton, currForm, form, input, uid;
  8702. uid = Basic.guid('uid_');
  8703. shimContainer = I.getShimContainer(); // we get new ref everytime to avoid memory leaks in IE
  8704. if (_uid) { // move previous form out of the view
  8705. currForm = Dom.get(_uid + '_form');
  8706. if (currForm) {
  8707. Basic.extend(currForm.style, { top: '100%' });
  8708. }
  8709. }
  8710. // build form in DOM, since innerHTML version not able to submit file for some reason
  8711. form = document.createElement('form');
  8712. form.setAttribute('id', uid + '_form');
  8713. form.setAttribute('method', 'post');
  8714. form.setAttribute('enctype', 'multipart/form-data');
  8715. form.setAttribute('encoding', 'multipart/form-data');
  8716. Basic.extend(form.style, {
  8717. overflow: 'hidden',
  8718. position: 'absolute',
  8719. top: 0,
  8720. left: 0,
  8721. width: '100%',
  8722. height: '100%'
  8723. });
  8724. input = document.createElement('input');
  8725. input.setAttribute('id', uid);
  8726. input.setAttribute('type', 'file');
  8727. input.setAttribute('name', _options.name || 'Filedata');
  8728. input.setAttribute('accept', _mimes.join(','));
  8729. Basic.extend(input.style, {
  8730. fontSize: '999px',
  8731. opacity: 0
  8732. });
  8733. form.appendChild(input);
  8734. shimContainer.appendChild(form);
  8735. // prepare file input to be placed underneath the browse_button element
  8736. Basic.extend(input.style, {
  8737. position: 'absolute',
  8738. top: 0,
  8739. left: 0,
  8740. width: '100%',
  8741. height: '100%'
  8742. });
  8743. if (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) {
  8744. Basic.extend(input.style, {
  8745. filter : "progid:DXImageTransform.Microsoft.Alpha(opacity=0)"
  8746. });
  8747. }
  8748. input.onchange = function() { // there should be only one handler for this
  8749. var file;
  8750. if (!this.value) {
  8751. return;
  8752. }
  8753. if (this.files) { // check if browser is fresh enough
  8754. file = this.files[0];
  8755. // ignore empty files (IE10 for example hangs if you try to send them via XHR)
  8756. if (file.size === 0) {
  8757. form.parentNode.removeChild(form);
  8758. return;
  8759. }
  8760. } else {
  8761. file = {
  8762. name: this.value
  8763. };
  8764. }
  8765. file = new File(I.uid, file);
  8766. // clear event handler
  8767. this.onchange = function() {};
  8768. addInput.call(comp);
  8769. comp.files = [file];
  8770. // substitute all ids with file uids (consider file.uid read-only - we cannot do it the other way around)
  8771. input.setAttribute('id', file.uid);
  8772. form.setAttribute('id', file.uid + '_form');
  8773. comp.trigger('change');
  8774. input = form = null;
  8775. };
  8776. // route click event to the input
  8777. if (I.can('summon_file_dialog')) {
  8778. browseButton = Dom.get(_options.browse_button);
  8779. Events.removeEvent(browseButton, 'click', comp.uid);
  8780. Events.addEvent(browseButton, 'click', function(e) {
  8781. if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
  8782. input.click();
  8783. }
  8784. e.preventDefault();
  8785. }, comp.uid);
  8786. }
  8787. _uid = uid;
  8788. shimContainer = currForm = browseButton = null;
  8789. }
  8790. Basic.extend(this, {
  8791. init: function(options) {
  8792. var comp = this, I = comp.getRuntime(), shimContainer;
  8793. // figure out accept string
  8794. _options = options;
  8795. _mimes = options.accept.mimes || Mime.extList2mimes(options.accept, I.can('filter_by_extension'));
  8796. shimContainer = I.getShimContainer();
  8797. (function() {
  8798. var browseButton, zIndex, top;
  8799. browseButton = Dom.get(options.browse_button);
  8800. // Route click event to the input[type=file] element for browsers that support such behavior
  8801. if (I.can('summon_file_dialog')) {
  8802. if (Dom.getStyle(browseButton, 'position') === 'static') {
  8803. browseButton.style.position = 'relative';
  8804. }
  8805. zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
  8806. browseButton.style.zIndex = zIndex;
  8807. shimContainer.style.zIndex = zIndex - 1;
  8808. }
  8809. /* Since we have to place input[type=file] on top of the browse_button for some browsers,
  8810. browse_button loses interactivity, so we restore it here */
  8811. top = I.can('summon_file_dialog') ? browseButton : shimContainer;
  8812. Events.addEvent(top, 'mouseover', function() {
  8813. comp.trigger('mouseenter');
  8814. }, comp.uid);
  8815. Events.addEvent(top, 'mouseout', function() {
  8816. comp.trigger('mouseleave');
  8817. }, comp.uid);
  8818. Events.addEvent(top, 'mousedown', function() {
  8819. comp.trigger('mousedown');
  8820. }, comp.uid);
  8821. Events.addEvent(Dom.get(options.container), 'mouseup', function() {
  8822. comp.trigger('mouseup');
  8823. }, comp.uid);
  8824. browseButton = null;
  8825. }());
  8826. addInput.call(this);
  8827. shimContainer = null;
  8828. // trigger ready event asynchronously
  8829. comp.trigger({
  8830. type: 'ready',
  8831. async: true
  8832. });
  8833. },
  8834. disable: function(state) {
  8835. var input;
  8836. if ((input = Dom.get(_uid))) {
  8837. input.disabled = !!state;
  8838. }
  8839. },
  8840. destroy: function() {
  8841. var I = this.getRuntime()
  8842. , shim = I.getShim()
  8843. , shimContainer = I.getShimContainer()
  8844. ;
  8845. Events.removeAllEvents(shimContainer, this.uid);
  8846. Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
  8847. Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
  8848. if (shimContainer) {
  8849. shimContainer.innerHTML = '';
  8850. }
  8851. shim.removeInstance(this.uid);
  8852. _uid = _mimes = _options = shimContainer = shim = null;
  8853. }
  8854. });
  8855. }
  8856. return (extensions.FileInput = FileInput);
  8857. });
  8858. // Included from: src/javascript/runtime/html4/file/FileReader.js
  8859. /**
  8860. * FileReader.js
  8861. *
  8862. * Copyright 2013, Moxiecode Systems AB
  8863. * Released under GPL License.
  8864. *
  8865. * License: http://www.plupload.com/license
  8866. * Contributing: http://www.plupload.com/contributing
  8867. */
  8868. /**
  8869. @class moxie/runtime/html4/file/FileReader
  8870. @private
  8871. */
  8872. define("moxie/runtime/html4/file/FileReader", [
  8873. "moxie/runtime/html4/Runtime",
  8874. "moxie/runtime/html5/file/FileReader"
  8875. ], function(extensions, FileReader) {
  8876. return (extensions.FileReader = FileReader);
  8877. });
  8878. // Included from: src/javascript/runtime/html4/xhr/XMLHttpRequest.js
  8879. /**
  8880. * XMLHttpRequest.js
  8881. *
  8882. * Copyright 2013, Moxiecode Systems AB
  8883. * Released under GPL License.
  8884. *
  8885. * License: http://www.plupload.com/license
  8886. * Contributing: http://www.plupload.com/contributing
  8887. */
  8888. /**
  8889. @class moxie/runtime/html4/xhr/XMLHttpRequest
  8890. @private
  8891. */
  8892. define("moxie/runtime/html4/xhr/XMLHttpRequest", [
  8893. "moxie/runtime/html4/Runtime",
  8894. "moxie/core/utils/Basic",
  8895. "moxie/core/utils/Dom",
  8896. "moxie/core/utils/Url",
  8897. "moxie/core/Exceptions",
  8898. "moxie/core/utils/Events",
  8899. "moxie/file/Blob",
  8900. "moxie/xhr/FormData"
  8901. ], function(extensions, Basic, Dom, Url, x, Events, Blob, FormData) {
  8902. function XMLHttpRequest() {
  8903. var _status, _response, _iframe;
  8904. function cleanup(cb) {
  8905. var target = this, uid, form, inputs, i, hasFile = false;
  8906. if (!_iframe) {
  8907. return;
  8908. }
  8909. uid = _iframe.id.replace(/_iframe$/, '');
  8910. form = Dom.get(uid + '_form');
  8911. if (form) {
  8912. inputs = form.getElementsByTagName('input');
  8913. i = inputs.length;
  8914. while (i--) {
  8915. switch (inputs[i].getAttribute('type')) {
  8916. case 'hidden':
  8917. inputs[i].parentNode.removeChild(inputs[i]);
  8918. break;
  8919. case 'file':
  8920. hasFile = true; // flag the case for later
  8921. break;
  8922. }
  8923. }
  8924. inputs = [];
  8925. if (!hasFile) { // we need to keep the form for sake of possible retries
  8926. form.parentNode.removeChild(form);
  8927. }
  8928. form = null;
  8929. }
  8930. // without timeout, request is marked as canceled (in console)
  8931. setTimeout(function() {
  8932. Events.removeEvent(_iframe, 'load', target.uid);
  8933. if (_iframe.parentNode) { // #382
  8934. _iframe.parentNode.removeChild(_iframe);
  8935. }
  8936. // check if shim container has any other children, if - not, remove it as well
  8937. var shimContainer = target.getRuntime().getShimContainer();
  8938. if (!shimContainer.children.length) {
  8939. shimContainer.parentNode.removeChild(shimContainer);
  8940. }
  8941. shimContainer = _iframe = null;
  8942. cb();
  8943. }, 1);
  8944. }
  8945. Basic.extend(this, {
  8946. send: function(meta, data) {
  8947. var target = this, I = target.getRuntime(), uid, form, input, blob;
  8948. _status = _response = null;
  8949. function createIframe() {
  8950. var container = I.getShimContainer() || document.body
  8951. , temp = document.createElement('div')
  8952. ;
  8953. // IE 6 won't be able to set the name using setAttribute or iframe.name
  8954. temp.innerHTML = '<iframe id="' + uid + '_iframe" name="' + uid + '_iframe" src="javascript:&quot;&quot;" style="display:none"></iframe>';
  8955. _iframe = temp.firstChild;
  8956. container.appendChild(_iframe);
  8957. /* _iframe.onreadystatechange = function() {
  8958. console.info(_iframe.readyState);
  8959. };*/
  8960. Events.addEvent(_iframe, 'load', function() { // _iframe.onload doesn't work in IE lte 8
  8961. var el;
  8962. try {
  8963. el = _iframe.contentWindow.document || _iframe.contentDocument || window.frames[_iframe.id].document;
  8964. // try to detect some standard error pages
  8965. if (/^4(0[0-9]|1[0-7]|2[2346])\s/.test(el.title)) { // test if title starts with 4xx HTTP error
  8966. _status = el.title.replace(/^(\d+).*$/, '$1');
  8967. } else {
  8968. _status = 200;
  8969. // get result
  8970. _response = Basic.trim(el.body.innerHTML);
  8971. // we need to fire these at least once
  8972. target.trigger({
  8973. type: 'progress',
  8974. loaded: _response.length,
  8975. total: _response.length
  8976. });
  8977. if (blob) { // if we were uploading a file
  8978. target.trigger({
  8979. type: 'uploadprogress',
  8980. loaded: blob.size || 1025,
  8981. total: blob.size || 1025
  8982. });
  8983. }
  8984. }
  8985. } catch (ex) {
  8986. if (Url.hasSameOrigin(meta.url)) {
  8987. // if response is sent with error code, iframe in IE gets redirected to res://ieframe.dll/http_x.htm
  8988. // which obviously results to cross domain error (wtf?)
  8989. _status = 404;
  8990. } else {
  8991. cleanup.call(target, function() {
  8992. target.trigger('error');
  8993. });
  8994. return;
  8995. }
  8996. }
  8997. cleanup.call(target, function() {
  8998. target.trigger('load');
  8999. });
  9000. }, target.uid);
  9001. } // end createIframe
  9002. // prepare data to be sent and convert if required
  9003. if (data instanceof FormData && data.hasBlob()) {
  9004. blob = data.getBlob();
  9005. uid = blob.uid;
  9006. input = Dom.get(uid);
  9007. form = Dom.get(uid + '_form');
  9008. if (!form) {
  9009. throw new x.DOMException(x.DOMException.NOT_FOUND_ERR);
  9010. }
  9011. } else {
  9012. uid = Basic.guid('uid_');
  9013. form = document.createElement('form');
  9014. form.setAttribute('id', uid + '_form');
  9015. form.setAttribute('method', meta.method);
  9016. form.setAttribute('enctype', 'multipart/form-data');
  9017. form.setAttribute('encoding', 'multipart/form-data');
  9018. I.getShimContainer().appendChild(form);
  9019. }
  9020. // set upload target
  9021. form.setAttribute('target', uid + '_iframe');
  9022. if (data instanceof FormData) {
  9023. data.each(function(value, name) {
  9024. if (value instanceof Blob) {
  9025. if (input) {
  9026. input.setAttribute('name', name);
  9027. }
  9028. } else {
  9029. var hidden = document.createElement('input');
  9030. Basic.extend(hidden, {
  9031. type : 'hidden',
  9032. name : name,
  9033. value : value
  9034. });
  9035. // make sure that input[type="file"], if it's there, comes last
  9036. if (input) {
  9037. form.insertBefore(hidden, input);
  9038. } else {
  9039. form.appendChild(hidden);
  9040. }
  9041. }
  9042. });
  9043. }
  9044. // set destination url
  9045. form.setAttribute("action", meta.url);
  9046. createIframe();
  9047. form.submit();
  9048. target.trigger('loadstart');
  9049. },
  9050. getStatus: function() {
  9051. return _status;
  9052. },
  9053. getResponse: function(responseType) {
  9054. if ('json' === responseType) {
  9055. // strip off <pre>..</pre> tags that might be enclosing the response
  9056. if (Basic.typeOf(_response) === 'string' && !!window.JSON) {
  9057. try {
  9058. return JSON.parse(_response.replace(/^\s*<pre[^>]*>/, '').replace(/<\/pre>\s*$/, ''));
  9059. } catch (ex) {
  9060. return null;
  9061. }
  9062. }
  9063. } else if ('document' === responseType) {
  9064. }
  9065. return _response;
  9066. },
  9067. abort: function() {
  9068. var target = this;
  9069. if (_iframe && _iframe.contentWindow) {
  9070. if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome
  9071. _iframe.contentWindow.stop();
  9072. } else if (_iframe.contentWindow.document.execCommand) { // IE
  9073. _iframe.contentWindow.document.execCommand('Stop');
  9074. } else {
  9075. _iframe.src = "about:blank";
  9076. }
  9077. }
  9078. cleanup.call(this, function() {
  9079. // target.dispatchEvent('readystatechange');
  9080. target.dispatchEvent('abort');
  9081. });
  9082. }
  9083. });
  9084. }
  9085. return (extensions.XMLHttpRequest = XMLHttpRequest);
  9086. });
  9087. // Included from: src/javascript/runtime/html4/image/Image.js
  9088. /**
  9089. * Image.js
  9090. *
  9091. * Copyright 2013, Moxiecode Systems AB
  9092. * Released under GPL License.
  9093. *
  9094. * License: http://www.plupload.com/license
  9095. * Contributing: http://www.plupload.com/contributing
  9096. */
  9097. /**
  9098. @class moxie/runtime/html4/image/Image
  9099. @private
  9100. */
  9101. define("moxie/runtime/html4/image/Image", [
  9102. "moxie/runtime/html4/Runtime",
  9103. "moxie/runtime/html5/image/Image"
  9104. ], function(extensions, Image) {
  9105. return (extensions.Image = Image);
  9106. });
  9107. expose(["moxie/core/utils/Basic","moxie/core/utils/Env","moxie/core/I18n","moxie/core/utils/Mime","moxie/core/utils/Dom","moxie/core/Exceptions","moxie/core/EventTarget","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/FileInput","moxie/core/utils/Encode","moxie/file/Blob","moxie/file/File","moxie/file/FileDrop","moxie/file/FileReader","moxie/core/utils/Url","moxie/runtime/RuntimeTarget","moxie/file/FileReaderSync","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/runtime/Transporter","moxie/image/Image","moxie/core/utils/Events"]);
  9108. })(this);
  9109. /**
  9110. * o.js
  9111. *
  9112. * Copyright 2013, Moxiecode Systems AB
  9113. * Released under GPL License.
  9114. *
  9115. * License: http://www.plupload.com/license
  9116. * Contributing: http://www.plupload.com/contributing
  9117. */
  9118. /*global moxie:true */
  9119. /**
  9120. Globally exposed namespace with the most frequently used public classes and handy methods.
  9121. @class o
  9122. @static
  9123. @private
  9124. */
  9125. (function(exports) {
  9126. "use strict";
  9127. var o = {}, inArray = exports.moxie.core.utils.Basic.inArray;
  9128. // directly add some public classes
  9129. // (we do it dynamically here, since for custom builds we cannot know beforehand what modules were included)
  9130. (function addAlias(ns) {
  9131. var name, itemType;
  9132. for (name in ns) {
  9133. itemType = typeof(ns[name]);
  9134. if (itemType === 'object' && !~inArray(name, ['Exceptions', 'Env', 'Mime'])) {
  9135. addAlias(ns[name]);
  9136. } else if (itemType === 'function') {
  9137. o[name] = ns[name];
  9138. }
  9139. }
  9140. })(exports.moxie);
  9141. // add some manually
  9142. o.Env = exports.moxie.core.utils.Env;
  9143. o.Mime = exports.moxie.core.utils.Mime;
  9144. o.Exceptions = exports.moxie.core.Exceptions;
  9145. // expose globally
  9146. exports.mOxie = o;
  9147. if (!exports.o) {
  9148. exports.o = o;
  9149. }
  9150. return o;
  9151. })(this);