legacy-debug.js 168 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955
  1. /**
  2. * SQL proxy lets you store data in a SQL database.
  3. * The Sencha Touch SQL proxy outputs model data into an HTML5
  4. * local database using WebSQL.
  5. *
  6. * You can create a Store for the proxy, for example:
  7. *
  8. * Ext.require(["Ext.data.proxy.SQL"]);
  9. *
  10. * Ext.define("User", {
  11. * extend: "Ext.data.Model",
  12. * config: {
  13. * fields: [ "firstName", "lastName" ]
  14. * }
  15. * });
  16. *
  17. * Ext.create("Ext.data.Store", {
  18. * model: "User",
  19. * storeId: "Users",
  20. * proxy: {
  21. * type: "sql"
  22. * }
  23. * });
  24. *
  25. * Ext.getStore("Users").add({
  26. * firstName: "Polly",
  27. * lastName: "Hedra"
  28. * });
  29. *
  30. * Ext.getStore("Users").sync();
  31. *
  32. * To destroy a table use:
  33. *
  34. * Ext.getStore("Users").getProxy().dropTable();
  35. *
  36. * To recreate a table use:
  37. *
  38. * Ext.data.Store.sync() or Ext.data.Model.save()
  39. */
  40. Ext.define('Ext.data.proxy.Sql', {
  41. alias: 'proxy.sql',
  42. extend: 'Ext.data.proxy.Client',
  43. alternateClassName: 'Ext.data.proxy.SQL',
  44. isSQLProxy: true,
  45. config: {
  46. /**
  47. * @cfg {Object} reader
  48. * @hide
  49. */
  50. reader: null,
  51. /**
  52. * @cfg {Object} writer
  53. * @hide
  54. */
  55. writer: null,
  56. /**
  57. * @cfg {String} table
  58. * Optional Table name to use if not provided ModelName will be used
  59. */
  60. table: null,
  61. /**
  62. * @cfg {String} database
  63. * Database name to access tables from
  64. */
  65. database: 'Sencha'
  66. },
  67. _createOptions: {
  68. silent: true,
  69. dirty: false
  70. },
  71. updateModel: function(model) {
  72. var me = this,
  73. modelName, len, i, columns, quoted;
  74. if (model) {
  75. me.uniqueIdStrategy = model.identifier.isUnique;
  76. if (!me.getTable()) {
  77. modelName = model.entityName;
  78. me.setTable(modelName.slice(modelName.lastIndexOf('.') + 1));
  79. }
  80. me.columns = columns = me.getPersistedModelColumns(model);
  81. me.quotedColumns = quoted = [];
  82. for (i = 0 , len = columns.length; i < len; ++i) {
  83. quoted.push('"' + columns[i] + '"');
  84. }
  85. }
  86. me.callParent([
  87. model
  88. ]);
  89. },
  90. setException: function(operation, error) {
  91. operation.setException(error);
  92. },
  93. create: function(operation) {
  94. var me = this,
  95. records = operation.getRecords(),
  96. result, error;
  97. operation.setStarted();
  98. me.executeTransaction(function(transaction) {
  99. me.insertRecords(records, transaction, function(resultSet, statementError) {
  100. result = resultSet;
  101. error = statementError;
  102. });
  103. }, function(transactionError) {
  104. operation.setException(transactionError);
  105. }, function() {
  106. if (error) {
  107. operation.setException(statementError);
  108. } else {
  109. operation.process(result);
  110. }
  111. });
  112. },
  113. read: function(operation) {
  114. var me = this,
  115. model = me.getModel(),
  116. records = operation.getRecords(),
  117. record = records ? records[0] : null,
  118. result, error, id, params;
  119. if (record && !record.phantom) {
  120. id = record.getId();
  121. } else {
  122. id = operation.getId();
  123. }
  124. if (id !== undefined) {
  125. params = {
  126. idOnly: true,
  127. id: id
  128. };
  129. } else {
  130. params = {
  131. page: operation.getPage(),
  132. start: operation.getStart(),
  133. limit: operation.getLimit(),
  134. sorters: operation.getSorters(),
  135. filters: operation.getFilters()
  136. };
  137. }
  138. operation.setStarted();
  139. me.executeTransaction(function(transaction) {
  140. me.selectRecords(transaction, params, function(resultSet, statementError) {
  141. result = resultSet;
  142. error = statementError;
  143. });
  144. }, function(transactionError) {
  145. operation.setException(transactionError);
  146. }, function() {
  147. if (error) {
  148. operation.setException(statementError);
  149. } else {
  150. operation.process(result);
  151. }
  152. });
  153. },
  154. update: function(operation) {
  155. var me = this,
  156. records = operation.getRecords(),
  157. result, error;
  158. operation.setStarted();
  159. me.executeTransaction(function(transaction) {
  160. me.updateRecords(transaction, records, function(resultSet, statementError) {
  161. result = resultSet;
  162. error = statementError;
  163. });
  164. }, function(transactionError) {
  165. operation.setException(transactionError);
  166. }, function() {
  167. if (error) {
  168. operation.setException(statementError);
  169. } else {
  170. operation.process(result);
  171. }
  172. });
  173. },
  174. erase: function(operation) {
  175. var me = this,
  176. records = operation.getRecords(),
  177. result, error;
  178. operation.setStarted();
  179. me.executeTransaction(function(transaction) {
  180. me.destroyRecords(transaction, records, function(resultSet, statementError) {
  181. result = resultSet;
  182. error = statementError;
  183. });
  184. }, function(transactionError) {
  185. operation.setException(transactionError);
  186. }, function() {
  187. if (error) {
  188. operation.setException(error);
  189. } else {
  190. operation.process(result);
  191. }
  192. });
  193. },
  194. createTable: function(transaction) {
  195. var me = this;
  196. if (!transaction) {
  197. me.executeTransaction(function(transaction) {
  198. me.createTable(transaction);
  199. });
  200. return;
  201. }
  202. me.executeStatement(transaction, 'CREATE TABLE IF NOT EXISTS "' + me.getTable() + '" (' + me.getSchemaString() + ')', function() {
  203. me.tableExists = true;
  204. });
  205. },
  206. insertRecords: function(records, transaction, callback) {
  207. var me = this,
  208. columns = me.columns,
  209. totalRecords = records.length,
  210. executed = 0,
  211. uniqueIdStrategy = me.uniqueIdStrategy,
  212. setOptions = me._createOptions,
  213. len = records.length,
  214. i, record, placeholders, sql, data, values, errors, completeIf;
  215. completeIf = function(transaction) {
  216. ++executed;
  217. if (executed === totalRecords) {
  218. callback.call(me, new Ext.data.ResultSet({
  219. success: !errors
  220. }), errors);
  221. }
  222. };
  223. placeholders = Ext.String.repeat('?', columns.length, ',');
  224. sql = 'INSERT INTO "' + me.getTable() + '" (' + me.quotedColumns.join(',') + ') VALUES (' + placeholders + ')';
  225. for (i = 0; i < len; ++i) {
  226. record = records[i];
  227. data = me.getRecordData(record);
  228. values = me.getColumnValues(columns, data);
  229. // Capture the record in closure scope so we can access it later
  230. (function(record) {
  231. me.executeStatement(transaction, sql, values, function(transaction, resultSet) {
  232. if (!uniqueIdStrategy) {
  233. record.setId(resultSet.insertId, setOptions);
  234. }
  235. completeIf();
  236. }, function(transaction, error) {
  237. if (!errors) {
  238. errors = [];
  239. }
  240. errors.push(error);
  241. completeIf();
  242. });
  243. })(record);
  244. }
  245. },
  246. selectRecords: function(transaction, params, callback, scope) {
  247. var me = this,
  248. Model = me.getModel(),
  249. idProperty = Model.idProperty,
  250. sql = 'SELECT * FROM "' + me.getTable() + '"',
  251. filterStatement = ' WHERE ',
  252. sortStatement = ' ORDER BY ',
  253. values = [],
  254. sorters, filters, placeholder, i, len, result, filter, sorter, property, operator, value;
  255. if (params.idOnly) {
  256. sql += filterStatement + '"' + idProperty + '" = ?';
  257. values.push(params);
  258. } else {
  259. filters = params.filters;
  260. len = filters && filters.length;
  261. if (len) {
  262. for (i = 0; i < len; i++) {
  263. filter = filters[i];
  264. property = filter.getProperty();
  265. value = me.toSqlValue(filter.getValue(), Model.getField(property));
  266. operator = filter.getOperator();
  267. if (property !== null) {
  268. operator = operator || '=';
  269. placeholder = '?';
  270. if (operator === 'like' || (operator === '=' && filter.getAnyMatch())) {
  271. operator = 'LIKE';
  272. value = '%' + value + '%';
  273. }
  274. if (operator === 'in' || operator === 'notin') {
  275. if (operator === 'notin') {
  276. operator = 'not in';
  277. }
  278. placeholder = '(' + Ext.String.repeat('?', value.length, ',') + ')';
  279. values = values.concat(value);
  280. } else {
  281. values.push(value);
  282. }
  283. sql += filterStatement + '"' + property + '" ' + operator + ' ' + placeholder;
  284. filterStatement = ' AND ';
  285. }
  286. }
  287. }
  288. sorters = params.sorters;
  289. len = sorters && sorters.length;
  290. if (len) {
  291. for (i = 0; i < len; i++) {
  292. sorter = sorters[i];
  293. property = sorter.getProperty();
  294. if (property !== null) {
  295. sql += sortStatement + '"' + property + '" ' + sorter.getDirection();
  296. sortStatement = ', ';
  297. }
  298. }
  299. }
  300. // handle start, limit, sort, filter and group params
  301. if (params.page !== undefined) {
  302. sql += ' LIMIT ' + parseInt(params.start, 10) + ', ' + parseInt(params.limit, 10);
  303. }
  304. }
  305. me.executeStatement(transaction, sql, values, function(transaction, resultSet) {
  306. var rows = resultSet.rows,
  307. count = rows.length,
  308. records = [],
  309. fields = Model.fields,
  310. fieldsLen = fields.length,
  311. raw, data, i, len, j, field, name;
  312. for (i = 0 , len = count; i < len; ++i) {
  313. raw = rows.item(i);
  314. data = {};
  315. for (j = 0; j < fieldsLen; ++j) {
  316. field = fields[j];
  317. name = field.name;
  318. data[name] = me.fromSqlValue(raw[name], field);
  319. }
  320. records.push(new Model(data));
  321. }
  322. callback.call(me, new Ext.data.ResultSet({
  323. records: records,
  324. success: true,
  325. total: count,
  326. count: count
  327. }));
  328. }, function(transaction, error) {
  329. callback.call(me, new Ext.data.ResultSet({
  330. success: false,
  331. total: 0,
  332. count: 0
  333. }), error);
  334. });
  335. },
  336. updateRecords: function(transaction, records, callback) {
  337. var me = this,
  338. columns = me.columns,
  339. quotedColumns = me.quotedColumns,
  340. totalRecords = records.length,
  341. executed = 0,
  342. updates = [],
  343. setOptions = me._createOptions,
  344. len, i, record, placeholders, sql, data, values, errors, completeIf;
  345. completeIf = function(transaction) {
  346. ++executed;
  347. if (executed === totalRecords) {
  348. callback.call(me, new Ext.data.ResultSet({
  349. success: !errors
  350. }), errors);
  351. }
  352. };
  353. for (i = 0 , len = quotedColumns.length; i < len; i++) {
  354. updates.push(quotedColumns[i] + ' = ?');
  355. }
  356. sql = 'UPDATE "' + me.getTable() + '" SET ' + updates.join(', ') + ' WHERE "' + me.getModel().idProperty + '" = ?';
  357. for (i = 0 , len = records.length; i < len; ++i) {
  358. record = records[i];
  359. data = me.getRecordData(record);
  360. values = me.getColumnValues(columns, data);
  361. values.push(record.getId());
  362. // Capture the record in closure scope so we can access it later
  363. (function(record) {
  364. me.executeStatement(transaction, sql, values, function(transaction, resultSet) {
  365. completeIf();
  366. }, function(transaction, error) {
  367. if (!errors) {
  368. errors = [];
  369. }
  370. errors.push(error);
  371. completeIf();
  372. });
  373. })(record);
  374. }
  375. },
  376. destroyRecords: function(transaction, records, callback) {
  377. var me = this,
  378. table = me.getTable(),
  379. idProperty = me.getModel().idProperty,
  380. ids = [],
  381. values = [],
  382. destroyedRecords = [],
  383. len = records.length,
  384. idStr = '"' + idProperty + '" = ?',
  385. i, result, record, sql;
  386. for (i = 0; i < len; i++) {
  387. ids.push(idStr);
  388. values.push(records[i].getId());
  389. }
  390. sql = 'DELETE FROM "' + me.getTable() + '" WHERE ' + ids.join(' OR ');
  391. me.executeStatement(transaction, sql, values, function(transaction, resultSet) {
  392. callback.call(me, new Ext.data.ResultSet({
  393. success: true
  394. }));
  395. }, function(transaction, error) {
  396. callback.call(me, new Ext.data.ResultSet({
  397. success: false
  398. }), error);
  399. });
  400. },
  401. /**
  402. * Formats the data for each record before sending it to the server. This
  403. * method should be overridden to format the data in a way that differs from the default.
  404. * @param {Object} record The record that we are writing to the server.
  405. * @return {Object} An object literal of name/value keys to be written to the server.
  406. * By default this method returns the data property on the record.
  407. */
  408. getRecordData: function(record) {
  409. var me = this,
  410. fields = record.fields,
  411. idProperty = record.idProperty,
  412. uniqueIdStrategy = me.uniqueIdStrategy,
  413. data = {},
  414. len = fields.length,
  415. recordData = record.data,
  416. i, name, value, field;
  417. for (i = 0; i < len; ++i) {
  418. field = fields[i];
  419. if (field.persist !== false) {
  420. name = field.name;
  421. if (name === idProperty && !uniqueIdStrategy) {
  422. continue;
  423. }
  424. data[name] = me.toSqlValue(recordData[name], field);
  425. }
  426. }
  427. return data;
  428. },
  429. getColumnValues: function(columns, data) {
  430. var len = columns.length,
  431. values = [],
  432. i, column, value;
  433. for (i = 0; i < len; i++) {
  434. column = columns[i];
  435. value = data[column];
  436. if (value !== undefined) {
  437. values.push(value);
  438. }
  439. }
  440. return values;
  441. },
  442. getSchemaString: function() {
  443. var me = this,
  444. schema = [],
  445. model = me.getModel(),
  446. idProperty = model.idProperty,
  447. fields = model.fields,
  448. uniqueIdStrategy = me.uniqueIdStrategy,
  449. len = fields.length,
  450. i, field, type, name;
  451. for (i = 0; i < len; i++) {
  452. field = fields[i];
  453. type = field.getType();
  454. name = field.name;
  455. if (name === idProperty) {
  456. if (uniqueIdStrategy) {
  457. type = me.convertToSqlType(type);
  458. schema.unshift('"' + idProperty + '" ' + type);
  459. } else {
  460. schema.unshift('"' + idProperty + '" INTEGER PRIMARY KEY AUTOINCREMENT');
  461. }
  462. } else {
  463. type = me.convertToSqlType(type);
  464. schema.push('"' + name + '" ' + type);
  465. }
  466. }
  467. return schema.join(', ');
  468. },
  469. convertToSqlType: function(type) {
  470. switch (type.toLowerCase()) {
  471. case 'string':
  472. case 'auto':
  473. return 'TEXT';
  474. case 'int':
  475. case 'date':
  476. return 'INTEGER';
  477. case 'float':
  478. return 'REAL';
  479. case 'bool':
  480. return 'NUMERIC';
  481. }
  482. },
  483. dropTable: function() {
  484. var me = this;
  485. me.executeTransaction(function(transaction) {
  486. me.executeStatement(transaction, 'DROP TABLE "' + me.getTable() + '"', function() {
  487. me.tableExists = false;
  488. });
  489. }, null, null, false);
  490. },
  491. getDatabaseObject: function() {
  492. return window.openDatabase(this.getDatabase(), '1.0', 'Sencha Database', 5 * 1024 * 1024);
  493. },
  494. privates: {
  495. executeStatement: function(transaction, sql, values, success, failure) {
  496. var me = this;
  497. transaction.executeSql(sql, values, success ? function() {
  498. success.apply(me, arguments);
  499. } : null, failure ? function() {
  500. failure.apply(me, arguments);
  501. } : null);
  502. },
  503. executeTransaction: function(runner, failure, success, autoCreateTable) {
  504. var me = this;
  505. autoCreateTable = autoCreateTable !== false;
  506. me.getDatabaseObject().transaction(runner ? function(transaction) {
  507. if (autoCreateTable && !me.tableExists) {
  508. me.createTable(transaction);
  509. }
  510. runner.apply(me, arguments);
  511. } : null, failure ? function() {
  512. failure.apply(me, arguments);
  513. } : null, success ? function() {
  514. success.apply(me, arguments);
  515. } : null);
  516. },
  517. fromSqlValue: function(value, field) {
  518. if (field.isDateField) {
  519. value = value ? new Date(value) : null;
  520. } else if (field.isBooleanField) {
  521. value = value === 1;
  522. }
  523. return value;
  524. },
  525. getPersistedModelColumns: function(model) {
  526. var fields = model.fields,
  527. uniqueIdStrategy = this.uniqueIdStrategy,
  528. idProperty = model.idProperty,
  529. columns = [],
  530. len = fields.length,
  531. i, field, name;
  532. for (i = 0; i < len; ++i) {
  533. field = fields[i];
  534. name = field.name;
  535. if (name === idProperty && !uniqueIdStrategy) {
  536. continue;
  537. }
  538. if (field.persist !== false) {
  539. columns.push(field.name);
  540. }
  541. }
  542. return columns;
  543. },
  544. toSqlValue: function(value, field) {
  545. if (field.isDateField) {
  546. value = value ? value.getTime() : null;
  547. } else if (field.isBooleanField) {
  548. value = value ? 1 : 0;
  549. }
  550. return value;
  551. }
  552. }
  553. });
  554. /**
  555. * @private
  556. */
  557. Ext.define('Ext.device.accelerometer.Abstract', {
  558. config: {
  559. /**
  560. * @cfg {Number} frequency The default frequency to get the current acceleration when using {@link Ext.device.Accelerometer#watchAcceleration}.
  561. */
  562. frequency: 10000
  563. },
  564. getCurrentAcceleration: function(config) {
  565. // <debug>
  566. if (!config.success) {
  567. Ext.Logger.warn('You need to specify a `success` function for #getCurrentAcceleration');
  568. }
  569. // </debug>
  570. return config;
  571. },
  572. watchAcceleration: function(config) {
  573. var defaultConfig = Ext.device.accelerometer.Abstract.prototype.config;
  574. config = Ext.applyIf(config, {
  575. frequency: defaultConfig.frequency
  576. });
  577. // <debug>
  578. if (!config.callback) {
  579. Ext.Logger.warn('You need to specify a `callback` function for #watchAcceleration');
  580. }
  581. // </debug>
  582. return config;
  583. },
  584. clearWatch: Ext.emptyFn
  585. });
  586. /**
  587. * @private
  588. */
  589. Ext.define('Ext.device.accelerometer.Cordova', {
  590. alternateClassName: 'Ext.device.accelerometer.PhoneGap',
  591. extend: 'Ext.device.accelerometer.Abstract',
  592. activeWatchID: null,
  593. getCurrentAcceleration: function(config) {
  594. config = this.callParent(arguments);
  595. navigator.accelerometer.getCurrentAcceleration(config.success, config.failure);
  596. return config;
  597. },
  598. watchAcceleration: function(config) {
  599. config = this.callParent(arguments);
  600. if (this.activeWatchID) {
  601. this.clearWatch();
  602. }
  603. this.activeWatchID = navigator.accelerometer.watchAcceleration(config.callback, config.failure, config);
  604. return config;
  605. },
  606. clearWatch: function() {
  607. if (this.activeWatchID) {
  608. navigator.accelerometer.clearWatch(this.activeWatchID);
  609. this.activeWatchID = null;
  610. }
  611. }
  612. });
  613. /**
  614. * @private
  615. */
  616. Ext.define('Ext.device.accelerometer.Simulator', {
  617. extend: 'Ext.device.accelerometer.Abstract'
  618. });
  619. /**
  620. * Provides access to the native Accelerometer API when running on a device. There are three implementations of this API:
  621. *
  622. * - [PhoneGap](http://docs.phonegap.com/en/2.6.0/cordova_accelerometer_accelerometer.md.html#Accelerometer)
  623. *
  624. * This class will automatically select the correct implementation depending on the device your application is running on.
  625. *
  626. * ## Examples
  627. *
  628. * Getting the current location:
  629. *
  630. * Ext.device.Accelerometer.getCurrentAcceleration({
  631. * success: function(acceleration) {
  632. * alert('Acceleration X: ' + acceleration.x + '\n' +
  633. * 'Acceleration Y: ' + acceleration.y + '\n' +
  634. * 'Acceleration Z: ' + acceleration.z + '\n' +
  635. * 'Timestamp: ' + acceleration.timestamp + '\n');
  636. * },
  637. * failure: function() {
  638. * console.log('something went wrong!');
  639. * }
  640. * });
  641. *
  642. * Watching the current acceleration:
  643. *
  644. * Ext.device.Accelerometer.watchAcceleration({
  645. * frequency: 500, // Update every 1/2 second
  646. * callback: function(acceleration) {
  647. * console.log('Acceleration X: ' + acceleration.x + '\n' +
  648. * 'Acceleration Y: ' + acceleration.y + '\n' +
  649. * 'Acceleration Z: ' + acceleration.z + '\n' +
  650. * 'Timestamp: ' + acceleration.timestamp + '\n');
  651. * },
  652. * failure: function() {
  653. * console.log('something went wrong!');
  654. * }
  655. * });
  656. *
  657. * @mixins Ext.device.accelerometer.Abstract
  658. */
  659. Ext.define('Ext.device.Accelerometer', {
  660. singleton: true,
  661. requires: [
  662. 'Ext.device.accelerometer.Cordova',
  663. 'Ext.device.accelerometer.Simulator'
  664. ],
  665. constructor: function() {
  666. var browserEnv = Ext.browser.is;
  667. if (browserEnv.WebView && browserEnv.Cordova) {
  668. return Ext.create('Ext.device.accelerometer.Cordova');
  669. }
  670. return Ext.create('Ext.device.accelerometer.Simulator');
  671. }
  672. });
  673. /**
  674. * @private
  675. *
  676. * This object handles communication between the WebView and Sencha's native shell.
  677. * Currently it has two primary responsibilities:
  678. *
  679. * 1. Maintaining unique string ids for callback functions, together with their scope objects
  680. * 2. Serializing given object data into HTTP GET request parameters
  681. *
  682. * As an example, to capture a photo from the device's camera, we use `Ext.device.Camera.capture()` like:
  683. *
  684. * Ext.device.Camera.capture(
  685. * function(dataUri){
  686. * // Do something with the base64-encoded `dataUri` string
  687. * },
  688. * function(errorMessage) {
  689. *
  690. * },
  691. * callbackScope,
  692. * {
  693. * quality: 75,
  694. * width: 500,
  695. * height: 500
  696. * }
  697. * );
  698. *
  699. * Internally, `Ext.device.Communicator.send()` will then be invoked with the following argument:
  700. *
  701. * Ext.device.Communicator.send({
  702. * command: 'Camera#capture',
  703. * callbacks: {
  704. * onSuccess: function() {
  705. * // ...
  706. * },
  707. * onError: function() {
  708. * // ...
  709. * }
  710. * },
  711. * scope: callbackScope,
  712. * quality: 75,
  713. * width: 500,
  714. * height: 500
  715. * });
  716. *
  717. * Which will then be transformed into a HTTP GET request, sent to native shell's local
  718. * HTTP server with the following parameters:
  719. *
  720. * ?quality=75&width=500&height=500&command=Camera%23capture&onSuccess=3&onError=5
  721. *
  722. * Notice that `onSuccess` and `onError` have been converted into string ids (`3` and `5`
  723. * respectively) and maintained by `Ext.device.Communicator`.
  724. *
  725. * Whenever the requested operation finishes, `Ext.device.Communicator.invoke()` simply needs
  726. * to be executed from the native shell with the corresponding ids given before. For example:
  727. *
  728. * Ext.device.Communicator.invoke('3', ['DATA_URI_OF_THE_CAPTURED_IMAGE_HERE']);
  729. *
  730. * will invoke the original `onSuccess` callback under the given scope. (`callbackScope`), with
  731. * the first argument of 'DATA_URI_OF_THE_CAPTURED_IMAGE_HERE'
  732. *
  733. * Note that `Ext.device.Communicator` maintains the uniqueness of each function callback and
  734. * its scope object. If subsequent calls to `Ext.device.Communicator.send()` have the same
  735. * callback references, the same old ids will simply be reused, which guarantee the best possible
  736. * performance for a large amount of repetitive calls.
  737. */
  738. Ext.define('Ext.device.communicator.Default', {
  739. SERVER_URL: 'http://localhost:3000',
  740. // Change this to the correct server URL
  741. callbackDataMap: {},
  742. callbackIdMap: {},
  743. idSeed: 0,
  744. globalScopeId: '0',
  745. generateId: function() {
  746. return String(++this.idSeed);
  747. },
  748. getId: function(object) {
  749. var id = object.$callbackId;
  750. if (!id) {
  751. object.$callbackId = id = this.generateId();
  752. }
  753. return id;
  754. },
  755. getCallbackId: function(callback, scope) {
  756. var idMap = this.callbackIdMap,
  757. dataMap = this.callbackDataMap,
  758. id, scopeId, callbackId, data;
  759. if (!scope) {
  760. scopeId = this.globalScopeId;
  761. } else if (scope.isIdentifiable) {
  762. scopeId = scope.getId();
  763. } else {
  764. scopeId = this.getId(scope);
  765. }
  766. callbackId = this.getId(callback);
  767. if (!idMap[scopeId]) {
  768. idMap[scopeId] = {};
  769. }
  770. if (!idMap[scopeId][callbackId]) {
  771. id = this.generateId();
  772. data = {
  773. callback: callback,
  774. scope: scope
  775. };
  776. idMap[scopeId][callbackId] = id;
  777. dataMap[id] = data;
  778. }
  779. return idMap[scopeId][callbackId];
  780. },
  781. getCallbackData: function(id) {
  782. return this.callbackDataMap[id];
  783. },
  784. invoke: function(id, args) {
  785. var data = this.getCallbackData(id);
  786. data.callback.apply(data.scope, args);
  787. },
  788. send: function(args) {
  789. var callbacks, scope, name, callback;
  790. if (!args) {
  791. args = {};
  792. } else if (args.callbacks) {
  793. callbacks = args.callbacks;
  794. scope = args.scope;
  795. delete args.callbacks;
  796. delete args.scope;
  797. for (name in callbacks) {
  798. if (callbacks.hasOwnProperty(name)) {
  799. callback = callbacks[name];
  800. if (typeof callback == 'function') {
  801. args[name] = this.getCallbackId(callback, scope);
  802. }
  803. }
  804. }
  805. }
  806. args.__source = document.location.href;
  807. var result = this.doSend(args);
  808. return (result && result.length > 0) ? JSON.parse(result) : null;
  809. },
  810. doSend: function(args) {
  811. var xhr = new XMLHttpRequest();
  812. xhr.open('GET', this.SERVER_URL + '?' + Ext.Object.toQueryString(args) + '&_dc=' + new Date().getTime(), false);
  813. // wrap the request in a try/catch block so we can check if any errors are thrown and attempt to call any
  814. // failure/callback functions if defined
  815. try {
  816. xhr.send(null);
  817. return xhr.responseText;
  818. } catch (e) {
  819. if (args.failure) {
  820. this.invoke(args.failure);
  821. } else if (args.callback) {
  822. this.invoke(args.callback);
  823. }
  824. }
  825. }
  826. });
  827. /**
  828. * @private
  829. */
  830. Ext.define('Ext.device.communicator.Android', {
  831. extend: 'Ext.device.communicator.Default',
  832. doSend: function(args) {
  833. return window.Sencha.action(JSON.stringify(args));
  834. }
  835. });
  836. /**
  837. * @private
  838. */
  839. Ext.define('Ext.device.Communicator', {
  840. requires: [
  841. 'Ext.device.communicator.Default',
  842. 'Ext.device.communicator.Android'
  843. ],
  844. singleton: true,
  845. constructor: function() {
  846. if (Ext.os.is.Android) {
  847. return new Ext.device.communicator.Android();
  848. }
  849. return new Ext.device.communicator.Default();
  850. }
  851. });
  852. /**
  853. * @private
  854. */
  855. Ext.define('Ext.device.analytics.Abstract', {
  856. config: {
  857. accountID: null
  858. },
  859. updateAccountID: function(newID) {
  860. if (newID) {
  861. window.plugins.googleAnalyticsPlugin.startTrackerWithAccountID(newID);
  862. }
  863. },
  864. /**
  865. * Registers yur Google Analytics account.
  866. *
  867. * @param {String} accountID Your Google Analytics account ID
  868. */
  869. registerAccount: function(accountID) {
  870. this.setAccountID(accountID);
  871. },
  872. /**
  873. * Track an event in your application.
  874. *
  875. * More information here: http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html
  876. *
  877. * @param {Object} config
  878. *
  879. * @param {String} config.category The name you supply for the group of objects you want to track
  880. *
  881. * @param {String} config.action A string that is uniquely paired with each category, and commonly
  882. * used to define the type of user interaction for the web object.
  883. *
  884. * @param {String} config.label An optional string to provide additional dimensions to the event data.
  885. *
  886. * @param {String} config.value An integer that you can use to provide numerical data about the user event
  887. *
  888. * @param {Boolean} config.nonInteraction A boolean that when set to true, indicates that the event hit will
  889. * not be used in bounce-rate calculation.
  890. */
  891. trackEvent: Ext.emptyFn,
  892. /**
  893. * Track an pageview in your application.
  894. *
  895. * @param {String} config.page The page you want to track (must start with a slash).
  896. */
  897. trackPageview: Ext.emptyFn
  898. });
  899. /**
  900. * @private
  901. */
  902. Ext.define('Ext.device.analytics.Cordova', {
  903. extend: 'Ext.device.analytics.Abstract',
  904. trackEvent: function(config) {
  905. if (!this.getAccountID()) {
  906. return;
  907. }
  908. window.plugins.googleAnalyticsPlugin.trackEvent(config.category, config.action, config.label, config.value, config.nonInteraction);
  909. },
  910. trackPageview: function(page) {
  911. if (!this.getAccountID()) {
  912. return;
  913. }
  914. window.plugins.googleAnalyticsPlugin.trackPageview(page);
  915. }
  916. });
  917. /**
  918. * Allows you to use Google Analytics within your Cordova application.
  919. *
  920. * For setup information, please read the [plugin documentation](https://github.com/phonegap/phonegap-facebook-plugin).
  921. *
  922. * @mixins Ext.device.analytics.Abstract
  923. */
  924. Ext.define('Ext.device.Analytics', {
  925. alternateClassName: 'Ext.ux.device.Analytics',
  926. singleton: true,
  927. requires: [
  928. 'Ext.device.Communicator',
  929. 'Ext.device.analytics.*'
  930. ],
  931. constructor: function() {
  932. var browserEnv = Ext.browser.is;
  933. if (browserEnv.WebView && browserEnv.Cordova) {
  934. return Ext.create('Ext.device.analytics.Cordova');
  935. } else {
  936. return Ext.create('Ext.device.analytics.Abstract');
  937. }
  938. }
  939. });
  940. /**
  941. * @private
  942. */
  943. Ext.define('Ext.device.browser.Abstract', {
  944. /**
  945. * Used to open a new browser window.
  946. *
  947. * When used with Cordova, a new InAppBrowser window opens. With Cordova, you also have the ability
  948. * to listen when the window starts loading, is finished loading, fails to load, and when it is closed.
  949. * You can also use the {@link #close} method to close the window, if opened.
  950. *
  951. * @param {Object} options
  952. * The options to use when opening a new browser window.
  953. *
  954. * @param {String} options.url
  955. * The URL to open.
  956. *
  957. * @param {Object} options.listeners
  958. * The listeners you want to add onto the window. Available events are:
  959. *
  960. * - `loadstart` - when the window starts loading the URL
  961. * - `loadstop` - when the window is finished loading the URL
  962. * - `loaderror` - when the window encounters an error loading the URL
  963. * - `close` - when the window is closed
  964. *
  965. * @param {Boolean} options.showToolbar
  966. * True to show the toolbar in the browser window.
  967. *
  968. * @param {String} options.options
  969. * A string of options which are used when using Cordova. For a full list of options, visit the
  970. * [PhoneGap documention](http://docs.phonegap.com/en/2.6.0/cordova_inappbrowser_inappbrowser.md.html#window.open).
  971. */
  972. open: Ext.emptyFn,
  973. /**
  974. * Used to close the browser, if one is opened.
  975. */
  976. close: Ext.emptyFn
  977. });
  978. /**
  979. * @private
  980. */
  981. Ext.define('Ext.device.browser.Cordova', {
  982. extend: 'Ext.device.browser.Abstract',
  983. open: function(config) {
  984. if (!this._window) {
  985. this._window = Ext.create('Ext.device.browser.Window');
  986. }
  987. this._window.open(config);
  988. return this._window;
  989. },
  990. close: function() {
  991. if (!this._window) {
  992. return;
  993. }
  994. this._window.close();
  995. }
  996. });
  997. /**
  998. * @private
  999. */
  1000. Ext.define('Ext.device.browser.Simulator', {
  1001. open: function(config) {
  1002. window.open(config.url, '_blank');
  1003. },
  1004. close: Ext.emptyFn
  1005. });
  1006. /**
  1007. * @mixins Ext.device.browser.Abstract
  1008. */
  1009. Ext.define('Ext.device.Browser', {
  1010. singleton: true,
  1011. requires: [
  1012. 'Ext.device.Communicator',
  1013. 'Ext.device.browser.Cordova',
  1014. 'Ext.device.browser.Simulator'
  1015. ],
  1016. constructor: function() {
  1017. var browserEnv = Ext.browser.is;
  1018. if (browserEnv.WebView && browserEnv.Cordova) {
  1019. return Ext.create('Ext.device.browser.Cordova');
  1020. }
  1021. return Ext.create('Ext.device.browser.Simulator');
  1022. }
  1023. });
  1024. /**
  1025. * @private
  1026. */
  1027. Ext.define('Ext.device.camera.Abstract', {
  1028. source: {
  1029. library: 0,
  1030. camera: 1,
  1031. album: 2
  1032. },
  1033. destination: {
  1034. data: 0,
  1035. // Returns base64-encoded string
  1036. file: 1,
  1037. // Returns file's URI
  1038. 'native': 2
  1039. },
  1040. encoding: {
  1041. jpeg: 0,
  1042. jpg: 0,
  1043. png: 1
  1044. },
  1045. media: {
  1046. picture: 0,
  1047. video: 1,
  1048. all: 2
  1049. },
  1050. direction: {
  1051. back: 0,
  1052. front: 1
  1053. },
  1054. /**
  1055. * Allows you to capture a photo.
  1056. *
  1057. * @param {Object} options
  1058. * The options to use when taking a photo.
  1059. *
  1060. * @param {Function} options.success
  1061. * The success callback which is called when the photo has been taken.
  1062. *
  1063. * @param {String} options.success.image
  1064. * The image which was just taken, either a base64 encoded string or a URI depending on which
  1065. * option you chose (destination).
  1066. *
  1067. * @param {Function} options.failure
  1068. * The function which is called when something goes wrong.
  1069. *
  1070. * @param {Object} scope
  1071. * The scope in which to call the `success` and `failure` functions, if specified.
  1072. *
  1073. * @param {Number} options.quality
  1074. * The quality of the image which is returned in the callback. This should be a percentage.
  1075. *
  1076. * @param {String} options.source
  1077. * The source of where the image should be taken. Available options are:
  1078. *
  1079. * - **album** - prompts the user to choose an image from an album
  1080. * - **camera** - prompts the user to take a new photo
  1081. * - **library** - prompts the user to choose an image from the library
  1082. *
  1083. * @param {String} destination
  1084. * The destination of the image which is returned. Available options are:
  1085. *
  1086. * - **data** - returns a base64 encoded string
  1087. * - **file** - returns the file's URI
  1088. *
  1089. * @param {String} encoding
  1090. * The encoding of the returned image. Available options are:
  1091. *
  1092. * - **jpg**
  1093. * - **png**
  1094. *
  1095. * @param {Number} width
  1096. * The width of the image to return
  1097. *
  1098. * @param {Number} height
  1099. * The height of the image to return
  1100. */
  1101. capture: Ext.emptyFn,
  1102. getPicture: Ext.emptyFn,
  1103. cleanup: Ext.emptyFn
  1104. });
  1105. /**
  1106. * @private
  1107. */
  1108. Ext.define('Ext.device.camera.Cordova', {
  1109. alternateClassName: 'Ext.device.camera.PhoneGap',
  1110. extend: 'Ext.device.camera.Abstract',
  1111. getPicture: function(onSuccess, onError, options) {
  1112. try {
  1113. navigator.camera.getPicture(onSuccess, onError, options);
  1114. } catch (e) {
  1115. alert(e);
  1116. }
  1117. },
  1118. cleanup: function(onSuccess, onError) {
  1119. try {
  1120. navigator.camera.cleanup(onSuccess, onError);
  1121. } catch (e) {
  1122. alert(e);
  1123. }
  1124. },
  1125. capture: function(args) {
  1126. var onSuccess = args.success,
  1127. onError = args.failure,
  1128. scope = args.scope,
  1129. sources = this.source,
  1130. destinations = this.destination,
  1131. encodings = this.encoding,
  1132. source = args.source,
  1133. destination = args.destination,
  1134. encoding = args.encoding,
  1135. options = {};
  1136. if (scope) {
  1137. onSuccess = Ext.Function.bind(onSuccess, scope);
  1138. onError = Ext.Function.bind(onError, scope);
  1139. }
  1140. if (source !== undefined) {
  1141. options.sourceType = sources.hasOwnProperty(source) ? sources[source] : source;
  1142. }
  1143. if (destination !== undefined) {
  1144. options.destinationType = destinations.hasOwnProperty(destination) ? destinations[destination] : destination;
  1145. }
  1146. if (encoding !== undefined) {
  1147. options.encodingType = encodings.hasOwnProperty(encoding) ? encodings[encoding] : encoding;
  1148. }
  1149. if ('quality' in args) {
  1150. options.quality = args.quality;
  1151. }
  1152. if ('width' in args) {
  1153. options.targetWidth = args.width;
  1154. }
  1155. if ('height' in args) {
  1156. options.targetHeight = args.height;
  1157. }
  1158. this.getPicture(onSuccess, onError, options);
  1159. }
  1160. });
  1161. /**
  1162. * @private
  1163. */
  1164. Ext.define('Ext.device.camera.Simulator', {
  1165. extend: 'Ext.device.camera.Abstract',
  1166. config: {
  1167. samples: [
  1168. {
  1169. success: 'http://www.sencha.com/img/sencha-large.png'
  1170. }
  1171. ]
  1172. },
  1173. constructor: function(config) {
  1174. this.initConfig(config);
  1175. },
  1176. updateSamples: function(samples) {
  1177. this.sampleIndex = 0;
  1178. },
  1179. capture: function(options) {
  1180. var index = this.sampleIndex,
  1181. samples = this.getSamples(),
  1182. samplesCount = samples.length,
  1183. sample = samples[index],
  1184. scope = options.scope,
  1185. success = options.success,
  1186. failure = options.failure;
  1187. if ('success' in sample) {
  1188. if (success) {
  1189. success.call(scope, sample.success);
  1190. }
  1191. } else {
  1192. if (failure) {
  1193. failure.call(scope, sample.failure);
  1194. }
  1195. }
  1196. if (++index > samplesCount - 1) {
  1197. index = 0;
  1198. }
  1199. this.sampleIndex = index;
  1200. }
  1201. });
  1202. /**
  1203. * This class allows you to use native APIs to take photos using the device camera.
  1204. *
  1205. * When this singleton is instantiated, it will automatically select the correct implementation depending on the
  1206. * current device:
  1207. *
  1208. * - Sencha Packager
  1209. * - Cordova
  1210. * - Simulator
  1211. *
  1212. * Both the Sencha Packager and Cordova implementations will use the native camera functionality to take or select
  1213. * a photo. The Simulator implementation will simply return fake images.
  1214. *
  1215. * ## Example
  1216. *
  1217. * You can use the {@link Ext.device.Camera#capture} function to take a photo:
  1218. *
  1219. * Ext.device.Camera.capture({
  1220. * success: function(image) {
  1221. * imageView.setSrc(image);
  1222. * },
  1223. * quality: 75,
  1224. * width: 200,
  1225. * height: 200,
  1226. * destination: 'data'
  1227. * });
  1228. *
  1229. * See the documentation for {@link Ext.device.Camera#capture} all available configurations.
  1230. *
  1231. * @mixins Ext.device.camera.Abstract
  1232. */
  1233. Ext.define('Ext.device.Camera', {
  1234. singleton: true,
  1235. requires: [
  1236. 'Ext.device.Communicator',
  1237. 'Ext.device.camera.Cordova',
  1238. 'Ext.device.camera.Simulator'
  1239. ],
  1240. constructor: function() {
  1241. var browserEnv = Ext.browser.is;
  1242. if (browserEnv.WebView) {
  1243. if (browserEnv.Cordova) {
  1244. return Ext.create('Ext.device.camera.Cordova');
  1245. }
  1246. }
  1247. return Ext.create('Ext.device.camera.Simulator');
  1248. }
  1249. });
  1250. /**
  1251. * @private
  1252. */
  1253. Ext.define('Ext.device.capture.Cordova', {
  1254. captureAudio: function(config) {
  1255. // <debug>
  1256. if (!config.success) {
  1257. Ext.Logger.warn('You need to specify a `success` function for #captureAudio');
  1258. }
  1259. // </debug>
  1260. var options = {
  1261. limit: config.limit,
  1262. duration: config.maximumDuration
  1263. };
  1264. navigator.device.capture.captureAudio(config.success, config.failure, options);
  1265. },
  1266. captureVideo: function(config) {
  1267. // <debug>
  1268. if (!config.success) {
  1269. Ext.Logger.warn('You need to specify a `success` function for #captureVideo');
  1270. }
  1271. // </debug>
  1272. var options = {
  1273. limit: config.limit,
  1274. duration: config.maximumDuration
  1275. };
  1276. navigator.device.capture.captureVideo(config.success, config.failure, options);
  1277. }
  1278. });
  1279. /**
  1280. * @private
  1281. */
  1282. Ext.define('Ext.device.capture.Abstract', {
  1283. alternateClassName: 'Ext.device.capture.Simulator',
  1284. /**
  1285. * Start the audio recorder application and return information about captured audio clip file(s).
  1286. *
  1287. * @example
  1288. * Ext.device.Capture.captureAudio({
  1289. * limit: 2, // limit to 2 recordings
  1290. * maximumDuration: 10, // limit to 10 seconds per recording
  1291. * success: function(files) {
  1292. * for (var i = 0; i < files.length; i++) {
  1293. * console.log('Captured audio path: ', files[i].fullPath);
  1294. * };
  1295. * },
  1296. * failure: function() {
  1297. * console.log('Something went wrong!');
  1298. * }
  1299. * });
  1300. *
  1301. * @param {Object} config The configuration object to be passed:
  1302. *
  1303. * @param {Number} config.limit The maximum number of recordings allowed (defaults to 1).
  1304. *
  1305. * @param {Number} config.maximumDuration The maximum duration of the capture, in seconds.
  1306. *
  1307. * @param {Number} config.duration The maximum duration of the capture, in seconds.
  1308. *
  1309. * @param {Function} config.success Called if the capture is successful.
  1310. * @param {Array} config.success.files An array of objects containing information about the captured audio.
  1311. *
  1312. * @param {Function} config.failure Called if the capture is unsuccessful.
  1313. */
  1314. captureAudio: Ext.emptyFn,
  1315. /**
  1316. * Start the video recorder application and return information about captured video clip file(s).
  1317. *
  1318. * @example
  1319. * Ext.device.Capture.captureVideo({
  1320. * limit: 2, // limit to 2 recordings
  1321. * maximumDuration: 10, // limit to 10 seconds per recording
  1322. * success: function(files) {
  1323. * for (var i = 0; i < files.length; i++) {
  1324. * console.log('Captured video path: ', files[i].fullPath);
  1325. * };
  1326. * },
  1327. * failure: function() {
  1328. * console.log('Something went wrong!');
  1329. * }
  1330. * });
  1331. *
  1332. * @param {Object} config The configuration object to be passed:
  1333. *
  1334. * @param {Number} config.limit The maximum number of recordings allowed (defaults to 1).
  1335. *
  1336. * @param {Number} config.maximumDuration The maximum duration of the capture, in seconds.
  1337. *
  1338. * @param {Number} config.duration The maximum duration of the capture, in seconds.
  1339. *
  1340. * @param {Function} config.success Called if the capture is successful.
  1341. * @param {Array} config.success.files An array of objects containing information about the captured video.
  1342. *
  1343. * @param {Function} config.failure Called if the capture is unsuccessful.
  1344. */
  1345. captureVideo: Ext.emptyFn
  1346. });
  1347. /**
  1348. * Provides access to the audio and video capture capabilities of the device.
  1349. *
  1350. * @mixins Ext.device.capture.Abstract
  1351. */
  1352. Ext.define('Ext.device.Capture', {
  1353. singleton: true,
  1354. requires: [
  1355. 'Ext.device.Communicator',
  1356. 'Ext.device.capture.Cordova',
  1357. 'Ext.device.capture.Simulator'
  1358. ],
  1359. constructor: function() {
  1360. var browserEnv = Ext.browser.is;
  1361. if (browserEnv.WebView && browserEnv.Cordova) {
  1362. return Ext.create('Ext.device.capture.Cordova');
  1363. }
  1364. return Ext.create('Ext.device.capture.Simulator');
  1365. }
  1366. });
  1367. /**
  1368. * @private
  1369. */
  1370. Ext.define('Ext.device.compass.Abstract', {
  1371. config: {
  1372. /**
  1373. * @cfg {Number} frequency The default frequency to get the current heading when using {@link Ext.device.Compass#watchHeading}.
  1374. */
  1375. frequency: 100
  1376. },
  1377. getHeadingAvailable: function(config) {
  1378. // <debug>
  1379. if (!config.callback) {
  1380. Ext.Logger.warn('You need to specify a `callback` function for #getHeadingAvailable');
  1381. }
  1382. // </debug>
  1383. return config;
  1384. },
  1385. getCurrentHeading: function(config) {
  1386. // <debug>
  1387. if (!config.success) {
  1388. Ext.Logger.warn('You need to specify a `success` function for #getCurrentHeading');
  1389. }
  1390. // </debug>
  1391. return config;
  1392. },
  1393. watchHeading: function(config) {
  1394. var defaultConfig = Ext.device.compass.Abstract.prototype.config;
  1395. config = Ext.applyIf(config, {
  1396. frequency: defaultConfig.frequency
  1397. });
  1398. // <debug>
  1399. if (!config.callback) {
  1400. Ext.Logger.warn('You need to specify a `callback` function for #watchHeading');
  1401. }
  1402. // </debug>
  1403. return config;
  1404. },
  1405. clearWatch: Ext.emptyFn
  1406. });
  1407. /**
  1408. * @private
  1409. */
  1410. Ext.define('Ext.device.compass.Cordova', {
  1411. alternateClassName: 'Ext.device.compass.PhoneGap',
  1412. extend: 'Ext.device.compass.Abstract',
  1413. activeWatchID: null,
  1414. getHeadingAvailable: function(config) {
  1415. var callback = function(result) {
  1416. if (result.hasOwnProperty("code")) {
  1417. config.callback.call(config.scope || this, false);
  1418. } else {
  1419. config.callback.call(config.scope || this, true);
  1420. }
  1421. };
  1422. this.getCurrentHeading({
  1423. success: callback,
  1424. failure: callback
  1425. });
  1426. },
  1427. getCurrentHeading: function(config) {
  1428. config = this.callParent(arguments);
  1429. navigator.compass.getCurrentHeading(config.success, config.failure);
  1430. return config;
  1431. },
  1432. watchHeading: function(config) {
  1433. config = this.callParent(arguments);
  1434. if (this.activeWatchID) {
  1435. this.clearWatch();
  1436. }
  1437. this.activeWatchID = navigator.compass.watchHeading(config.callback, config.failure, config);
  1438. return config;
  1439. },
  1440. clearWatch: function() {
  1441. if (this.activeWatchID) {
  1442. navigator.compass.clearWatch(this.activeWatchID);
  1443. this.activeWatchID = null;
  1444. }
  1445. }
  1446. });
  1447. /**
  1448. * @private
  1449. */
  1450. Ext.define('Ext.device.compass.Simulator', {
  1451. extend: 'Ext.device.compass.Abstract'
  1452. });
  1453. /**
  1454. * Provides access to the native Compass API when running on a device. There are three implementations of this API:
  1455. *
  1456. * - [PhoneGap](http://docs.phonegap.com/en/2.6.0/cordova_compass_compass.md.html#Compass)
  1457. *
  1458. * This class will automatically select the correct implementation depending on the device your application is running on.
  1459. *
  1460. * ## Examples
  1461. *
  1462. * Getting the current location:
  1463. *
  1464. * Ext.device.Compass.getCurrentHeading({
  1465. * success: function(heading) {
  1466. * alert('Heading: ' + heading.magneticHeading);
  1467. * },
  1468. * failure: function() {
  1469. * console.log('something went wrong!');
  1470. * }
  1471. * });
  1472. *
  1473. * Watching the current compass:
  1474. *
  1475. * Ext.device.Compass.watchHeading({
  1476. * frequency: 500, // Update every 1/2 second
  1477. * callback: function(heading) {
  1478. * console.log('Heading: ' + heading.magneticHeading);
  1479. * },
  1480. * failure: function() {
  1481. * console.log('something went wrong!');
  1482. * }
  1483. * });
  1484. *
  1485. * @mixins Ext.device.compass.Abstract
  1486. */
  1487. Ext.define('Ext.device.Compass', {
  1488. singleton: true,
  1489. requires: [
  1490. 'Ext.device.compass.Cordova',
  1491. 'Ext.device.compass.Simulator'
  1492. ],
  1493. constructor: function() {
  1494. var browserEnv = Ext.browser.is;
  1495. if (browserEnv.WebView && browserEnv.Cordova) {
  1496. return Ext.create('Ext.device.compass.Cordova');
  1497. }
  1498. return Ext.create('Ext.device.compass.Simulator');
  1499. }
  1500. });
  1501. /**
  1502. * @private
  1503. */
  1504. Ext.define('Ext.device.connection.Abstract', {
  1505. extend: 'Ext.Evented',
  1506. mixins: [
  1507. 'Ext.mixin.Observable'
  1508. ],
  1509. config: {
  1510. online: false,
  1511. type: null
  1512. },
  1513. /**
  1514. * @event online
  1515. * Fires when the device goes online
  1516. */
  1517. /**
  1518. * @event offline
  1519. * Fires when the device goes offline
  1520. */
  1521. /**
  1522. * @property {String} UNKNOWN
  1523. * Text label for a connection type.
  1524. */
  1525. UNKNOWN: 'Unknown connection',
  1526. /**
  1527. * @property {String} ETHERNET
  1528. * Text label for a connection type.
  1529. */
  1530. ETHERNET: 'Ethernet connection',
  1531. /**
  1532. * @property {String} WIFI
  1533. * Text label for a connection type.
  1534. */
  1535. WIFI: 'WiFi connection',
  1536. /**
  1537. * @property {String} CELL_2G
  1538. * Text label for a connection type.
  1539. */
  1540. CELL_2G: 'Cell 2G connection',
  1541. /**
  1542. * @property {String} CELL_3G
  1543. * Text label for a connection type.
  1544. */
  1545. CELL_3G: 'Cell 3G connection',
  1546. /**
  1547. * @property {String} CELL_4G
  1548. * Text label for a connection type.
  1549. */
  1550. CELL_4G: 'Cell 4G connection',
  1551. /**
  1552. * @property {String} NONE
  1553. * Text label for a connection type.
  1554. */
  1555. NONE: 'No network connection',
  1556. /**
  1557. * True if the device is currently online
  1558. * @return {Boolean} online
  1559. */
  1560. isOnline: function() {
  1561. return this.getOnline();
  1562. }
  1563. });
  1564. /**
  1565. * @method getType
  1566. * Returns the current connection type.
  1567. * @return {String} type
  1568. */
  1569. /**
  1570. * @private
  1571. */
  1572. Ext.define('Ext.device.connection.Cordova', {
  1573. alternateClassName: 'Ext.device.connection.PhoneGap',
  1574. extend: 'Ext.device.connection.Abstract',
  1575. constructor: function() {
  1576. var me = this;
  1577. document.addEventListener('online', function() {
  1578. me.fireEvent('online', me);
  1579. });
  1580. document.addEventListener('offline', function() {
  1581. me.fireEvent('offline', me);
  1582. });
  1583. },
  1584. syncOnline: function() {
  1585. var type = navigator.connection.type;
  1586. this._type = type;
  1587. this._online = type != Connection.NONE;
  1588. },
  1589. getOnline: function() {
  1590. this.syncOnline();
  1591. return this._online;
  1592. },
  1593. getType: function() {
  1594. this.syncOnline();
  1595. return this._type;
  1596. }
  1597. });
  1598. /**
  1599. * @private
  1600. */
  1601. Ext.define('Ext.device.connection.Simulator', {
  1602. extend: 'Ext.device.connection.Abstract',
  1603. getOnline: function() {
  1604. this._online = navigator.onLine;
  1605. this._type = Ext.device.Connection.UNKNOWN;
  1606. return this._online;
  1607. }
  1608. });
  1609. /**
  1610. * This class is used to check if the current device is currently online or not. It has three different implementations:
  1611. *
  1612. * - Sencha Packager
  1613. * - Cordova
  1614. * - Simulator
  1615. *
  1616. * Both the Sencha Packager and Cordova implementations will use the native functionality to determine if the current
  1617. * device is online. The Simulator version will simply use `navigator.onLine`.
  1618. *
  1619. * When this singleton ({@link Ext.device.Connection}) is instantiated, it will automatically decide which version to
  1620. * use based on the current platform.
  1621. *
  1622. * ## Examples
  1623. *
  1624. * Determining if the current device is online:
  1625. *
  1626. * alert(Ext.device.Connection.isOnline());
  1627. *
  1628. * Checking the type of connection the device has:
  1629. *
  1630. * alert('Your connection type is: ' + Ext.device.Connection.getType());
  1631. *
  1632. * The available connection types are:
  1633. *
  1634. * - {@link Ext.device.Connection#UNKNOWN UNKNOWN} - Unknown connection
  1635. * - {@link Ext.device.Connection#ETHERNET ETHERNET} - Ethernet connection
  1636. * - {@link Ext.device.Connection#WIFI WIFI} - WiFi connection
  1637. * - {@link Ext.device.Connection#CELL_2G CELL_2G} - Cell 2G connection
  1638. * - {@link Ext.device.Connection#CELL_3G CELL_3G} - Cell 3G connection
  1639. * - {@link Ext.device.Connection#CELL_4G CELL_4G} - Cell 4G connection
  1640. * - {@link Ext.device.Connection#NONE NONE} - No network connection
  1641. *
  1642. * @mixins Ext.device.connection.Abstract
  1643. */
  1644. Ext.define('Ext.device.Connection', {
  1645. singleton: true,
  1646. requires: [
  1647. 'Ext.device.Communicator',
  1648. 'Ext.device.connection.Cordova',
  1649. 'Ext.device.connection.Simulator'
  1650. ],
  1651. /**
  1652. * @event onlinechange
  1653. * @inheritdoc Ext.device.connection.Sencha#onlinechange
  1654. */
  1655. constructor: function() {
  1656. var browserEnv = Ext.browser.is;
  1657. if (browserEnv.WebView) {
  1658. if (browserEnv.Cordova) {
  1659. return Ext.create('Ext.device.connection.Cordova');
  1660. }
  1661. }
  1662. return Ext.create('Ext.device.connection.Simulator');
  1663. }
  1664. });
  1665. /**
  1666. * @private
  1667. */
  1668. Ext.define('Ext.device.contacts.Abstract', {
  1669. mixins: [
  1670. 'Ext.mixin.Observable'
  1671. ],
  1672. config: {
  1673. /**
  1674. * @cfg {Boolean} includeImages
  1675. * True to include images when you get the contacts store. Please beware that this can be very slow.
  1676. */
  1677. includeImages: false
  1678. },
  1679. /**
  1680. * Returns an Array of contact objects.
  1681. * @return {Object[]} An array of contact objects.
  1682. */
  1683. getContacts: function(config) {
  1684. if (!this._store) {
  1685. this._store = [
  1686. {
  1687. first: 'Peter',
  1688. last: 'Venkman',
  1689. emails: {
  1690. work: 'peter.venkman@gb.com'
  1691. }
  1692. },
  1693. {
  1694. first: 'Egon',
  1695. last: 'Spengler',
  1696. emails: {
  1697. work: 'egon.spengler@gb.com'
  1698. }
  1699. }
  1700. ];
  1701. }
  1702. config.success.call(config.scope || this, this._store);
  1703. },
  1704. /**
  1705. * Returns base64 encoded image thumbnail for a contact specified in config.id
  1706. * **This method is for Sencha Native Packager only**
  1707. *
  1708. * @return {String} base64 string
  1709. */
  1710. getThumbnail: function(config) {
  1711. config.callback.call(config.scope || this, "");
  1712. },
  1713. /**
  1714. * Returns localized, user readable label for a contact field (i.e. "Mobile", "Home")
  1715. * **This method is for Sencha Native Packager only**
  1716. *
  1717. * @return {String} user readable string
  1718. */
  1719. getLocalizedLabel: function(config) {
  1720. config.callback.call(config.scope || this, config.label.toUpperCase(), config.label);
  1721. }
  1722. });
  1723. /**
  1724. * @private
  1725. */
  1726. Ext.define('Ext.device.contacts.Cordova', {
  1727. alternateClassName: 'Ext.device.contacts.PhoneGap',
  1728. extend: 'Ext.device.contacts.Abstract',
  1729. getContacts: function(config) {
  1730. if (!config) {
  1731. Ext.Logger.warn('Ext.device.Contacts#getContacts: You must specify a `config` object.');
  1732. return false;
  1733. }
  1734. if (!config.success) {
  1735. Ext.Logger.warn('Ext.device.Contacts#getContacts: You must specify a `success` method.');
  1736. return false;
  1737. }
  1738. if (!config.fields) {
  1739. config.fields = [
  1740. "*"
  1741. ];
  1742. }
  1743. if (!Ext.isArray(config.fields)) {
  1744. config.fields = [
  1745. config.fields
  1746. ];
  1747. }
  1748. if (Ext.isEmpty(config.multiple)) {
  1749. config.multiple = true;
  1750. }
  1751. navigator.contacts.find(config.fields, config.success, config.failure, config);
  1752. }
  1753. });
  1754. /**
  1755. * This device API allows you to access a users contacts using a {@link Ext.data.Store}. This allows you to search, filter
  1756. * and sort through all the contacts using its methods.
  1757. *
  1758. * To use this API, all you need to do is require this class (`Ext.device.Contacts`) and then use `Ext.device.Contacts.getContacts()`
  1759. * to retrieve an array of contacts.
  1760. *
  1761. * **Please note that getThumbnail and getLocalizedLabel are *only* for the Sencha Native Packager.**
  1762. * **Both Cordova/PhoneGap and Sencha Native Packager can access the find method though properties of returned contacts will differ.**
  1763. *
  1764. * # Example
  1765. *
  1766. * Ext.application({
  1767. * name: 'Sencha',
  1768. * requires: 'Ext.device.Contacts',
  1769. *
  1770. * launch: function() {
  1771. * Ext.Viewport.add({
  1772. * xtype: 'list',
  1773. * itemTpl: '{First} {Last}',
  1774. * store: {
  1775. * fields: ['First', 'Last'],
  1776. * data: Ext.device.Contacts.getContacts()
  1777. * }
  1778. * });
  1779. * }
  1780. * });
  1781. *
  1782. * @mixins Ext.device.contacts.Abstract
  1783. * @mixins Ext.device.contacts.Sencha
  1784. * @mixins Ext.device.contacts.Cordova
  1785. */
  1786. Ext.define('Ext.device.Contacts', {
  1787. singleton: true,
  1788. requires: [
  1789. 'Ext.device.Communicator',
  1790. 'Ext.device.contacts.Cordova'
  1791. ],
  1792. constructor: function() {
  1793. var browserEnv = Ext.browser.is;
  1794. if (browserEnv.WebView) {
  1795. if (browserEnv.Cordova) {
  1796. return Ext.create('Ext.device.contacts.Cordova');
  1797. }
  1798. }
  1799. return Ext.create('Ext.device.contacts.Abstract');
  1800. }
  1801. });
  1802. /**
  1803. * @private
  1804. */
  1805. Ext.define('Ext.device.device.Abstract', {
  1806. mixins: [
  1807. 'Ext.mixin.Observable'
  1808. ],
  1809. /**
  1810. * @event schemeupdate
  1811. * Event which is fired when your Sencha Native packaged application is opened from another application using a custom URL scheme.
  1812. *
  1813. * This event will only fire if the application was already open (in other words; `onReady` was already fired). This means you should check
  1814. * if {@link Ext.device.Device#scheme} is set in your Application `launch`/`onReady` method, and perform any needed changes for that URL (if defined).
  1815. * Then listen to this event for future changed.
  1816. *
  1817. * ## Example
  1818. *
  1819. * Ext.application({
  1820. * name: 'Sencha',
  1821. * requires: ['Ext.device.Device'],
  1822. * launch: function() {
  1823. * if (Ext.device.Device.scheme) {
  1824. * // the application was opened via another application. Do something:
  1825. * console.log('Applicaton opened via another application: ' + Ext.device.Device.scheme.url);
  1826. * }
  1827. *
  1828. * // Listen for future changes
  1829. * Ext.device.Device.on('schemeupdate', function(device, scheme) {
  1830. * // the application was launched, closed, and then launched another from another application
  1831. * // this means onReady wont be called again ('cause the application is already running in the
  1832. * // background) - but this event will be fired
  1833. * console.log('Applicated reopened via another application: ' + scheme.url);
  1834. * }, this);
  1835. * }
  1836. * });
  1837. *
  1838. * __Note:__ This currently only works with the Sencha Native Packager. If you attempt to listen to this event when packaged with
  1839. * PhoneGap or simply in the browser, it will never fire.**
  1840. *
  1841. * @param {Ext.device.Device} this The instance of Ext.device.Device
  1842. * @param {Object/Boolean} scheme The scheme information, if opened via another application
  1843. * @param {String} scheme.url The URL that was opened, if this application was opened via another application. Example: `sencha:`
  1844. * @param {String} scheme.sourceApplication The source application that opened this application. Example: `com.apple.safari`.
  1845. */
  1846. /**
  1847. * @property {String} name
  1848. * Returns the name of the current device. If the current device does not have a name (for example, in a browser), it will
  1849. * default to `not available`.
  1850. *
  1851. * alert('Device name: ' + Ext.device.Device.name);
  1852. */
  1853. name: 'not available',
  1854. /**
  1855. * @property {String} uuid
  1856. * Returns a unique identifier for the current device. If the current device does not have a unique identifier (for example,
  1857. * in a browser), it will default to `anonymous`.
  1858. *
  1859. * alert('Device UUID: ' + Ext.device.Device.uuid);
  1860. */
  1861. uuid: 'anonymous',
  1862. /**
  1863. * @property {String} platform
  1864. * The current platform the device is running on.
  1865. *
  1866. * alert('Device platform: ' + Ext.device.Device.platform);
  1867. */
  1868. platform: Ext.os.name,
  1869. /**
  1870. * @property {Object/Boolean} scheme
  1871. *
  1872. */
  1873. scheme: false,
  1874. /**
  1875. * Opens a specified URL. The URL can contain a custom URL Scheme for another app or service:
  1876. *
  1877. * // Safari
  1878. * Ext.device.Device.openURL('http://sencha.com');
  1879. *
  1880. * // Telephone
  1881. * Ext.device.Device.openURL('tel:6501231234');
  1882. *
  1883. * // SMS with a default number
  1884. * Ext.device.Device.openURL('sms:+12345678901');
  1885. *
  1886. * // Email client
  1887. * Ext.device.Device.openURL('mailto:rob@sencha.com');
  1888. *
  1889. * You can find a full list of available URL schemes here: [http://wiki.akosma.com/IPhone_URL_Schemes](http://wiki.akosma.com/IPhone_URL_Schemes).
  1890. *
  1891. * __Note:__ This currently only works with the Sencha Native Packager. Attempting to use this on PhoneGap, iOS Simulator
  1892. * or the browser will simply result in the current window location changing.**
  1893. *
  1894. * If successful, this will close the application (as another one opens).
  1895. *
  1896. * @param {String} url The URL to open
  1897. */
  1898. openURL: function(url) {
  1899. window.location = url;
  1900. }
  1901. });
  1902. /**
  1903. * @private
  1904. */
  1905. Ext.define('Ext.device.device.Cordova', {
  1906. alternateClassName: 'Ext.device.device.PhoneGap',
  1907. extend: 'Ext.device.device.Abstract',
  1908. availableListeners: [
  1909. 'pause',
  1910. 'resume',
  1911. 'backbutton',
  1912. 'batterycritical',
  1913. 'batterylow',
  1914. 'batterystatus',
  1915. 'menubutton',
  1916. 'searchbutton',
  1917. 'startcallbutton',
  1918. 'endcallbutton',
  1919. 'volumeupbutton',
  1920. 'volumedownbutton'
  1921. ],
  1922. constructor: function() {
  1923. // We can't get the device details until the device is ready, so lets wait.
  1924. if (Ext.isReady) {
  1925. this.onReady();
  1926. } else {
  1927. Ext.onReady(this.onReady, this, {
  1928. single: true
  1929. });
  1930. }
  1931. },
  1932. /**
  1933. * @property {String} cordova
  1934. * Returns the version of Cordova running on the device.
  1935. *
  1936. * alert('Device cordova: ' + Ext.device.Device.cordova);
  1937. */
  1938. /**
  1939. * @property {String} version
  1940. * Returns the operating system version.
  1941. *
  1942. * alert('Device Version: ' + Ext.device.Device.version);
  1943. */
  1944. /**
  1945. * @property {String} model
  1946. * Returns the device's model name.
  1947. *
  1948. * alert('Device Model: ' + Ext.device.Device.model);
  1949. */
  1950. /**
  1951. * @event pause
  1952. * Fires when the application goes into the background
  1953. */
  1954. /**
  1955. * @event resume
  1956. * Fires when the application goes into the foreground
  1957. */
  1958. /**
  1959. * @event batterycritical
  1960. * This event that fires when a Cordova application detects the percentage of battery
  1961. * has reached the critical battery threshold.
  1962. */
  1963. /**
  1964. * @event batterylow
  1965. * This event that fires when a Cordova application detects the percentage of battery
  1966. * has reached the low battery threshold.
  1967. */
  1968. /**
  1969. * @event batterystatus
  1970. * This event that fires when a Cordova application detects the percentage of battery
  1971. * has changed by at least 1 percent.
  1972. */
  1973. /**
  1974. * @event backbutton
  1975. * This is an event that fires when the user presses the back button.
  1976. */
  1977. /**
  1978. * @event menubutton
  1979. * This is an event that fires when the user presses the menu button.
  1980. */
  1981. /**
  1982. * @event searchbutton
  1983. * This is an event that fires when the user presses the search button.
  1984. */
  1985. /**
  1986. * @event startcallbutton
  1987. * This is an event that fires when the user presses the start call button.
  1988. */
  1989. /**
  1990. * @event endcallbutton
  1991. * This is an event that fires when the user presses the end call button.
  1992. */
  1993. /**
  1994. * @event volumeupbutton
  1995. * This is an event that fires when the user presses the volume up button.
  1996. */
  1997. /**
  1998. * @event volumedownbutton
  1999. * This is an event that fires when the user presses the volume down button.
  2000. */
  2001. onReady: function() {
  2002. var me = this,
  2003. device = window.device;
  2004. me.name = device.name || device.model;
  2005. me.cordova = device.cordova;
  2006. me.platform = device.platform || Ext.os.name;
  2007. me.uuid = device.uuid;
  2008. me.version = device.version;
  2009. me.model = device.model;
  2010. },
  2011. privates: {
  2012. doAddListener: function(name) {
  2013. var me = this;
  2014. if (!me.addedListeners) {
  2015. me.addedListeners = [];
  2016. }
  2017. if (me.availableListeners.indexOf(name) != -1 && me.addedListeners.indexOf(name) == -1) {
  2018. // Add the listeners
  2019. me.addedListeners.push(name);
  2020. document.addEventListener(name, function() {
  2021. me.fireEvent(name, me);
  2022. });
  2023. }
  2024. Ext.device.Device.mixins.observable.doAddListener.apply(Ext.device.Device.mixins.observable, arguments);
  2025. }
  2026. }
  2027. });
  2028. /**
  2029. * @private
  2030. */
  2031. Ext.define('Ext.device.device.Simulator', {
  2032. extend: 'Ext.device.device.Abstract'
  2033. });
  2034. /**
  2035. * Provides a cross device way to get information about the device your application is running on. There are 3 different implementations:
  2036. *
  2037. * - Sencha Packager
  2038. * - [Cordova](http://cordova.apache.org/docs/en/2.5.0/cordova_device_device.md.html#Device)
  2039. * - Simulator
  2040. *
  2041. * ## Examples
  2042. *
  2043. * #### Device Information
  2044. *
  2045. * Getting the device information:
  2046. *
  2047. * Ext.application({
  2048. * name: 'Sencha',
  2049. *
  2050. * // Remember that the Ext.device.Device class *must* be required
  2051. * requires: ['Ext.device.Device'],
  2052. *
  2053. * launch: function() {
  2054. * alert([
  2055. * 'Device name: ' + Ext.device.Device.name,
  2056. * 'Device platform: ' + Ext.device.Device.platform,
  2057. * 'Device UUID: ' + Ext.device.Device.uuid
  2058. * ].join('\n'));
  2059. * }
  2060. * });
  2061. *
  2062. * ### Custom Scheme URL
  2063. *
  2064. * Using custom scheme URL to application your application from other applications:
  2065. *
  2066. * Ext.application({
  2067. * name: 'Sencha',
  2068. * requires: ['Ext.device.Device'],
  2069. * launch: function() {
  2070. * if (Ext.device.Device.scheme) {
  2071. * // the application was opened via another application. Do something:
  2072. * alert('Applicaton pened via another application: ' + Ext.device.Device.scheme.url);
  2073. * }
  2074. *
  2075. * // Listen for future changes
  2076. * Ext.device.Device.on('schemeupdate', function(device, scheme) {
  2077. * // the application was launched, closed, and then launched another from another application
  2078. * // this means onReady wont be called again ('cause the application is already running in the
  2079. * // background) - but this event will be fired
  2080. * alert('Applicated reopened via another application: ' + scheme.url);
  2081. * }, this);
  2082. * }
  2083. * });
  2084. *
  2085. * Of course, you must add the custom scheme URL you would like to use when packaging your application.
  2086. * You can do this by setting the `URLScheme` property inside your `package.json` file (Sencha Native Packager configuration file):
  2087. *
  2088. * {
  2089. * ...
  2090. * "URLScheme": "sencha",
  2091. * ...
  2092. * }
  2093. *
  2094. * You can change the available URL scheme.
  2095. *
  2096. * You can then test it by packaging and installing the application onto a device/iOS Simulator, opening Safari and typing: `sencha:testing`.
  2097. * The application will launch and it will `alert` the URL you specified.
  2098. *
  2099. * **PLEASE NOTE: This currently only works with the Sencha Native Packager. If you attempt to listen to this event when packaged with
  2100. * PhoneGap or simply in the browser, it will not function.**
  2101. *
  2102. * @mixins Ext.device.device.Abstract
  2103. */
  2104. Ext.define('Ext.device.Device', {
  2105. singleton: true,
  2106. requires: [
  2107. 'Ext.device.Communicator',
  2108. 'Ext.device.device.Cordova',
  2109. 'Ext.device.device.Simulator'
  2110. ],
  2111. constructor: function() {
  2112. var browserEnv = Ext.browser.is;
  2113. if (browserEnv.WebView) {
  2114. if (browserEnv.Cordova) {
  2115. return Ext.create('Ext.device.device.Cordova');
  2116. }
  2117. }
  2118. return Ext.create('Ext.device.device.Simulator');
  2119. }
  2120. });
  2121. /**
  2122. * @private
  2123. */
  2124. Ext.define('Ext.device.filesystem.Abstract', {
  2125. config: {
  2126. fileSystemType: 1,
  2127. fileSystemSize: 0,
  2128. readerType: "text",
  2129. stringEncoding: "UTF8"
  2130. },
  2131. requestFileSystem: function(config) {
  2132. var defaultConfig = Ext.device.filesystem.Abstract.prototype.config;
  2133. config = Ext.applyIf(config, {
  2134. type: defaultConfig.fileSystemType,
  2135. size: defaultConfig.fileSystemSize,
  2136. success: Ext.emptyFn,
  2137. failure: Ext.emptyFn
  2138. });
  2139. return config;
  2140. }
  2141. });
  2142. /**
  2143. * @private
  2144. */
  2145. Ext.define('Ext.device.filesystem.HTML5', {
  2146. extend: 'Ext.device.filesystem.Abstract',
  2147. /**
  2148. * Requests a {@link Ext.device.filesystem.FileSystem} instance.
  2149. *
  2150. * var me = this;
  2151. * var fs = Ext.create("Ext.device.FileSystem", {});
  2152. * fs.requestFileSystem({
  2153. * type: window.PERSISTENT,
  2154. * size: 1024 * 1024,
  2155. * success: function(fileSystem) {
  2156. * me.fs = fileSystem;
  2157. * },
  2158. * failure: function(err) {
  2159. * console.log("FileSystem Failure: " + err.code);
  2160. * }
  2161. * });
  2162. *
  2163. * @param {Object} config
  2164. * The object which contains the following config options:
  2165. *
  2166. * @param {Number} config.type
  2167. * window.TEMPORARY (0) or window.PERSISTENT (1)
  2168. *
  2169. * @param {Number} config.size
  2170. * Storage space, in Bytes, needed by the application
  2171. *
  2172. * @param {Function} config.success This is required.
  2173. * The callback to be called when the file system has been successfully created.
  2174. *
  2175. * @param {Ext.device.filesystem.FileSystem} config.success.fileSystem
  2176. * The created file system.
  2177. *
  2178. * @param {Function} config.failure This is optional.
  2179. * The callback to be called when an error occurred.
  2180. *
  2181. * @param {Object} config.failure.error
  2182. * The occurred error.
  2183. *
  2184. * @param {Object} config.scope
  2185. * The scope object
  2186. */
  2187. requestFileSystem: function(config) {
  2188. if (!config.success) {
  2189. Ext.Logger.error('Ext.device.filesystem#requestFileSystem: You must specify a `success` callback.');
  2190. return null;
  2191. }
  2192. var me = this;
  2193. var successCallback = function(fs) {
  2194. var fileSystem = Ext.create('Ext.device.filesystem.FileSystem', fs);
  2195. config.success.call(config.scope || me, fileSystem);
  2196. };
  2197. window.requestFileSystem(config.type, config.size, successCallback, config.failure || Ext.emptyFn);
  2198. }
  2199. }, function() {
  2200. /**
  2201. * The FileSystem class which is used to represent a file system.
  2202. */
  2203. Ext.define('Ext.device.filesystem.FileSystem', {
  2204. fs: null,
  2205. root: null,
  2206. constructor: function(fs) {
  2207. this.fs = fs;
  2208. this.root = Ext.create('Ext.device.filesystem.DirectoryEntry', '/', this);
  2209. },
  2210. /**
  2211. * Returns a {@link Ext.device.filesystem.DirectoryEntry} instance for the root of the file system.
  2212. *
  2213. * @return {Ext.device.filesystem.DirectoryEntry}
  2214. * The file system root directory.
  2215. */
  2216. getRoot: function() {
  2217. return this.root;
  2218. }
  2219. }, function() {
  2220. /**
  2221. * The Entry class which is used to represent entries in a file system,
  2222. * each of which may be a {@link Ext.device.filesystem.FileEntry} or a {@link Ext.device.filesystem.DirectoryEntry}.
  2223. *
  2224. * This is an abstract class.
  2225. * @abstract
  2226. */
  2227. Ext.define('Ext.device.filesystem.Entry', {
  2228. directory: false,
  2229. path: 0,
  2230. fileSystem: null,
  2231. entry: null,
  2232. constructor: function(directory, path, fileSystem) {
  2233. this.directory = directory;
  2234. this.path = path;
  2235. this.fileSystem = fileSystem;
  2236. },
  2237. /**
  2238. * Returns whether the entry is a file.
  2239. *
  2240. * @return {Boolean}
  2241. * The entry is a file.
  2242. */
  2243. isFile: function() {
  2244. return !this.directory;
  2245. },
  2246. /**
  2247. * Returns whether the entry is a directory.
  2248. *
  2249. * @return {Boolean}
  2250. * The entry is a directory.
  2251. */
  2252. isDirectory: function() {
  2253. return this.directory;
  2254. },
  2255. /**
  2256. * Returns the name of the entry, excluding the path leading to it.
  2257. *
  2258. * @return {String}
  2259. * The entry name.
  2260. */
  2261. getName: function() {
  2262. var components = this.path.split('/');
  2263. for (var i = components.length - 1; i >= 0; --i) {
  2264. if (components[i].length > 0) {
  2265. return components[i];
  2266. }
  2267. }
  2268. return '/';
  2269. },
  2270. /**
  2271. * Returns the full absolute path from the root to the entry.
  2272. *
  2273. * @return {String}
  2274. * The entry full path.
  2275. */
  2276. getFullPath: function() {
  2277. return this.path;
  2278. },
  2279. /**
  2280. * Returns the file system on which the entry resides.
  2281. *
  2282. * @return {Ext.device.filesystem.FileSystem}
  2283. * The entry file system.
  2284. */
  2285. getFileSystem: function() {
  2286. return this.fileSystem;
  2287. },
  2288. getEntry: function() {
  2289. return null;
  2290. },
  2291. /**
  2292. * Moves the entry to a different location on the file system.
  2293. *
  2294. * @param {Object} config
  2295. * The object which contains the following config options:
  2296. *
  2297. * @param {Ext.device.filesystem.DirectoryEntry} config.parent This is required.
  2298. * The directory to which to move the entry.
  2299. *
  2300. * @param {String} config.newName This is optional.
  2301. * The new name of the entry to move. Defaults to the entry's current name if unspecified.
  2302. *
  2303. * @param {Function} config.success This is optional.
  2304. * The callback to be called when the entry has been successfully moved.
  2305. *
  2306. * @param {Ext.device.filesystem.Entry} config.success.entry
  2307. * The entry for the new location.
  2308. *
  2309. * @param {Function} config.failure This is optional.
  2310. * The callback to be called when an error occurred.
  2311. *
  2312. * @param {Object} config.failure.error
  2313. * The occurred error.
  2314. *
  2315. * @param {Object} config.scope
  2316. * The scope object
  2317. */
  2318. moveTo: function(config) {
  2319. if (config.parent == null) {
  2320. Ext.Logger.error('Ext.device.filesystem.Entry#moveTo: You must specify a new `parent` of the entry.');
  2321. return null;
  2322. }
  2323. var me = this;
  2324. this.getEntry({
  2325. options: config.options || {},
  2326. success: function(sourceEntry) {
  2327. config.parent.getEntry({
  2328. options: config.options || {},
  2329. success: function(destinationEntry) {
  2330. if (config.copy) {
  2331. sourceEntry.copyTo(destinationEntry, config.newName, function(entry) {
  2332. config.success.call(config.scope || me, entry.isDirectory ? Ext.create('Ext.device.filesystem.DirectoryEntry', entry.fullPath, me.fileSystem) : Ext.create('Ext.device.filesystem.FileEntry', entry.fullPath, me.fileSystem));
  2333. }, config.failure);
  2334. } else {
  2335. sourceEntry.moveTo(destinationEntry, config.newName, function(entry) {
  2336. config.success.call(config.scope || me, entry.isDirectory ? Ext.create('Ext.device.filesystem.DirectoryEntry', entry.fullPath, me.fileSystem) : Ext.create('Ext.device.filesystem.FileEntry', entry.fullPath, me.fileSystem));
  2337. }, config.failure);
  2338. }
  2339. },
  2340. failure: config.failure
  2341. });
  2342. },
  2343. failure: config.failure
  2344. });
  2345. },
  2346. /**
  2347. * Works the same way as {@link Ext.device.filesystem.Entry#moveTo}, but copies the entry.
  2348. */
  2349. copyTo: function(config) {
  2350. this.moveTo(Ext.apply(config, {
  2351. copy: true
  2352. }));
  2353. },
  2354. /**
  2355. * Removes the entry from the file system.
  2356. *
  2357. * @param {Object} config
  2358. * The object which contains the following config options:
  2359. *
  2360. * @param {Boolean} config.recursively This is optional
  2361. * Deletes a directory and all of its contents
  2362. *
  2363. * @param {Function} config.success This is optional.
  2364. * The callback to be called when the entry has been successfully removed.
  2365. *
  2366. * @param {Function} config.failure This is optional.
  2367. * The callback to be called when an error occurred.
  2368. *
  2369. * @param {Object} config.failure.error
  2370. * The occurred error.
  2371. *
  2372. * @param {Object} config.scope
  2373. * The scope object
  2374. */
  2375. remove: function(config) {
  2376. this.getEntry({
  2377. success: function(entry) {
  2378. if (config.recursively && this.directory) {
  2379. entry.removeRecursively(config.success, config.failure);
  2380. } else {
  2381. entry.remove(config.success, config.failure);
  2382. }
  2383. },
  2384. failure: config.failure
  2385. });
  2386. },
  2387. /**
  2388. * Looks up the parent directory containing the entry.
  2389. *
  2390. * @param {Object} config
  2391. * The object which contains the following config options:
  2392. *
  2393. * @param {Function} config.success This is required.
  2394. * The callback to be called when the parent directory has been successfully selected.
  2395. *
  2396. * @param {Ext.device.filesystem.DirectoryEntry} config.success.entry
  2397. * The parent directory of the entry.
  2398. *
  2399. * @param {Function} config.failure This is optional.
  2400. * The callback to be called when an error occurred.
  2401. *
  2402. * @param {Object} config.failure.error
  2403. * The occurred error.
  2404. *
  2405. * @param {Object} config.scope
  2406. * The scope object
  2407. */
  2408. getParent: function(config) {
  2409. if (!config.success) {
  2410. Ext.Logger.error('Ext.device.filesystem.Entry#getParent: You must specify a `success` callback.');
  2411. return null;
  2412. }
  2413. var me = this;
  2414. this.getEntry({
  2415. options: config.options || {},
  2416. success: function(entry) {
  2417. entry.getParent(function(parentEntry) {
  2418. config.success.call(config.scope || me, parentEntry.isDirectory ? Ext.create('Ext.device.filesystem.DirectoryEntry', parentEntry.fullPath, me.fileSystem) : Ext.create('Ext.device.filesystem.FileEntry', parentEntry.fullPath, me.fileSystem));
  2419. }, config.failure);
  2420. },
  2421. failure: config.failure
  2422. });
  2423. }
  2424. });
  2425. /**
  2426. * The DirectoryEntry class which is used to represent a directory on a file system.
  2427. */
  2428. Ext.define('Ext.device.filesystem.DirectoryEntry', {
  2429. extend: 'Ext.device.filesystem.Entry',
  2430. cachedDirectory: null,
  2431. constructor: function(path, fileSystem) {
  2432. this.callParent([
  2433. true,
  2434. path,
  2435. fileSystem
  2436. ]);
  2437. },
  2438. /**
  2439. * Requests a Directory from the Local File System
  2440. *
  2441. * @param {Object} config
  2442. *
  2443. * @param {Object} config.options
  2444. * File creation options {create:true, exclusive:false}
  2445. *
  2446. * @param {Boolean} config.options.create
  2447. * Indicates if the directory should be created if it doesn't exist
  2448. *
  2449. * @param {Boolean} config.options.exclusive
  2450. * Used with the create option only indicates whether a creation causes an error if the directory already exists
  2451. *
  2452. * @param {Function} config.success
  2453. * The function called when the Directory is returned successfully
  2454. *
  2455. * @param {Ext.device.filesystem.DirectoryEntry} config.success.directory
  2456. * DirectoryEntry Object
  2457. *
  2458. * @param {Function} config.failure
  2459. * The function called when the Directory request causes an error
  2460. *
  2461. * @param {FileError} config.failure.error
  2462. */
  2463. getEntry: function(config) {
  2464. var me = this;
  2465. var callback = config.success;
  2466. if ((config.options && config.options.create) && this.path) {
  2467. var folders = this.path.split("/");
  2468. if (folders[0] == '.' || folders[0] == '') {
  2469. folders = folders.slice(1);
  2470. }
  2471. var recursiveCreation = function(dirEntry) {
  2472. if (folders.length) {
  2473. dirEntry.getDirectory(folders.shift(), config.options, recursiveCreation, config.failure);
  2474. } else {
  2475. callback(dirEntry);
  2476. }
  2477. };
  2478. recursiveCreation(this.fileSystem.fs.root);
  2479. } else {
  2480. this.fileSystem.fs.root.getDirectory(this.path, config.options, function(directory) {
  2481. config.success.call(config.scope || me, directory);
  2482. }, config.failure);
  2483. }
  2484. },
  2485. /**
  2486. * Lists all the entries in the directory.
  2487. *
  2488. * @param {Object} config
  2489. * The object which contains the following config options:
  2490. *
  2491. * @param {Function} config.success This is required.
  2492. * The callback to be called when the entries has been successfully read.
  2493. *
  2494. * @param {Ext.device.filesystem.Entry[]} config.success.entries
  2495. * The array of entries of the directory.
  2496. *
  2497. * @param {Function} config.failure This is optional.
  2498. * The callback to be called when an error occurred.
  2499. *
  2500. * @param {Object} config.failure.error
  2501. * The occurred error.
  2502. *
  2503. * @param {Object} config.scope
  2504. * The scope object
  2505. */
  2506. readEntries: function(config) {
  2507. if (!config.success) {
  2508. Ext.Logger.error('Ext.device.filesystem.DirectoryEntry#readEntries: You must specify a `success` callback.');
  2509. return null;
  2510. }
  2511. var me = this;
  2512. this.getEntry({
  2513. success: function(dirEntry) {
  2514. var directoryReader = dirEntry.createReader();
  2515. directoryReader.readEntries(function(entryInfos) {
  2516. var entries = [],
  2517. i = 0,
  2518. len = entryInfos.length;
  2519. for (; i < len; i++) {
  2520. entryInfo = entryInfos[i];
  2521. entries[i] = entryInfo.isDirectory ? Ext.create('Ext.device.filesystem.DirectoryEntry', entryInfo.fullPath, me.fileSystem) : Ext.create('Ext.device.filesystem.FileEntry', entryInfo.fullPath, me.fileSystem);
  2522. }
  2523. config.success.call(config.scope || this, entries);
  2524. }, function(error) {
  2525. if (config.failure) {
  2526. config.failure.call(config.scope || this, error);
  2527. }
  2528. });
  2529. },
  2530. failure: config.failure
  2531. });
  2532. },
  2533. /**
  2534. * Creates or looks up a file.
  2535. *
  2536. * @param {Object} config
  2537. * The object which contains the following config options:
  2538. *
  2539. * @param {String} config.path This is required.
  2540. * The absolute path or relative path from the entry to the file to create or select.
  2541. *
  2542. * @param {Object} config.options This is optional.
  2543. * The object which contains the following options:
  2544. *
  2545. * @param {Boolean} config.options.create This is optional.
  2546. * Indicates whether to create a file, if path does not exist.
  2547. *
  2548. * @param {Boolean} config.options.exclusive This is optional. Used with 'create', by itself has no effect.
  2549. * Indicates that method should fail, if path already exists.
  2550. *
  2551. * @param {Function} config.success This is optional.
  2552. * The callback to be called when the file has been successfully created or selected.
  2553. *
  2554. * @param {Ext.device.filesystem.Entry} config.success.entry
  2555. * The created or selected file.
  2556. *
  2557. * @param {Function} config.failure This is optional.
  2558. * The callback to be called when an error occurred.
  2559. *
  2560. * @param {Object} config.failure.error
  2561. * The occurred error.
  2562. *
  2563. * @param {Object} config.scope
  2564. * The scope object
  2565. */
  2566. getFile: function(config) {
  2567. if (config.path == null) {
  2568. Ext.Logger.error('Ext.device.filesystem.DirectoryEntry#getFile: You must specify a `path` of the file.');
  2569. return null;
  2570. }
  2571. var me = this;
  2572. var fullPath = this.path + config.path;
  2573. var fileEntry = Ext.create('Ext.device.filesystem.FileEntry', fullPath, this.fileSystem);
  2574. fileEntry.getEntry({
  2575. success: function() {
  2576. config.success.call(config.scope || me, fileEntry);
  2577. },
  2578. options: config.options || {},
  2579. failure: config.failure
  2580. });
  2581. },
  2582. /**
  2583. * Works the same way as {@link Ext.device.filesystem.DirectoryEntry#getFile},
  2584. * but creates or looks up a directory.
  2585. */
  2586. getDirectory: function(config) {
  2587. if (config.path == null) {
  2588. Ext.Logger.error('Ext.device.filesystem.DirectoryEntry#getFile: You must specify a `path` of the file.');
  2589. return null;
  2590. }
  2591. var me = this;
  2592. var fullPath = this.path + config.path;
  2593. var directoryEntry = Ext.create('Ext.device.filesystem.DirectoryEntry', fullPath, this.fileSystem);
  2594. directoryEntry.getEntry({
  2595. success: function() {
  2596. config.success.call(config.scope || me, directoryEntry);
  2597. },
  2598. options: config.options || {},
  2599. failure: config.failure
  2600. });
  2601. },
  2602. /**
  2603. * Works the same way as {@link Ext.device.filesystem.Entry#remove},
  2604. * but removes the directory and all of its contents, if any.
  2605. */
  2606. removeRecursively: function(config) {
  2607. this.remove(Ext.apply(config, {
  2608. recursively: true
  2609. }));
  2610. }
  2611. });
  2612. /**
  2613. * The FileEntry class which is used to represent a file on a file system.
  2614. */
  2615. Ext.define('Ext.device.filesystem.FileEntry', {
  2616. extend: 'Ext.device.filesystem.Entry',
  2617. length: 0,
  2618. offset: 0,
  2619. constructor: function(path, fileSystem) {
  2620. this.callParent([
  2621. false,
  2622. path,
  2623. fileSystem
  2624. ]);
  2625. this.offset = 0;
  2626. this.length = 0;
  2627. },
  2628. /**
  2629. * Requests a File Handle from the Local File System
  2630. *
  2631. * @param {Object} config
  2632. *
  2633. * @param {String} config.file
  2634. * Filename optionally including path in string format '/tmp/debug.txt' or a File Object
  2635. *
  2636. * @param {Object} config.options
  2637. * File creation options {create:true, exclusive:false}
  2638. *
  2639. * @param {Boolean} config.options.create
  2640. * Indicates if the file should be created if it doesn't exist
  2641. *
  2642. * @param {Boolean} config.options.exclusive
  2643. * Used with the create option only indicates whether a creation causes an error if the file already exists
  2644. *
  2645. * @param {Function} config.success
  2646. * The function called when the filesystem is returned successfully
  2647. *
  2648. * @param {FileSystem} config.success.entry
  2649. *
  2650. * @param {Function} config.failure
  2651. * The function called when the filesystem request causes and error
  2652. *
  2653. * @param {FileError} config.failure.error
  2654. *
  2655. */
  2656. getEntry: function(config) {
  2657. var me = this;
  2658. var originalConfig = Ext.applyIf({}, config);
  2659. if (this.fileSystem) {
  2660. var failure = function(evt) {
  2661. if ((config.options && config.options.create) && Ext.isString(this.path)) {
  2662. var folders = this.path.split("/");
  2663. if (folders[0] == '.' || folders[0] == '') {
  2664. folders = folders.slice(1);
  2665. }
  2666. if (folders.length > 1 && !config.recursive === true) {
  2667. folders.pop();
  2668. var dirEntry = Ext.create('Ext.device.filesystem.DirectoryEntry', folders.join("/"), me.fileSystem);
  2669. dirEntry.getEntry({
  2670. options: config.options,
  2671. success: function() {
  2672. originalConfig.recursive = true;
  2673. me.getEntry(originalConfig);
  2674. },
  2675. failure: config.failure
  2676. });
  2677. } else {
  2678. if (config.failure) {
  2679. config.failure.call(config.scope || me, evt);
  2680. }
  2681. }
  2682. } else {
  2683. if (config.failure) {
  2684. config.failure.call(config.scope || me, evt);
  2685. }
  2686. }
  2687. };
  2688. this.fileSystem.fs.root.getFile(this.path, config.options || null, function(fileEntry) {
  2689. fileEntry.file(function(file) {
  2690. me.length = file.size;
  2691. originalConfig.success.call(config.scope || me, fileEntry);
  2692. }, function(error) {
  2693. failure.call(config.scope || me, error);
  2694. });
  2695. }, function(error) {
  2696. failure.call(config.scope || me, error);
  2697. });
  2698. } else {
  2699. config.failure({
  2700. code: -1,
  2701. message: "FileSystem not Initialized"
  2702. });
  2703. }
  2704. },
  2705. /**
  2706. * Returns the byte offset into the file at which the next read/write will occur.
  2707. *
  2708. * @return {Number}
  2709. * The file offset.
  2710. */
  2711. getOffset: function() {
  2712. return this.offset;
  2713. },
  2714. /**
  2715. * Sets the byte offset into the file at which the next read/write will occur.
  2716. *
  2717. * @param {Object} config
  2718. * The object which contains the following config options:
  2719. *
  2720. * @param {Number} config.offset This is required.
  2721. * The file offset to set. If negative, the offset back from the end of the file.
  2722. *
  2723. * @param {Function} config.success This is optional.
  2724. * The callback to be called when the file offset has been successfully set.
  2725. *
  2726. * @param {Function} config.failure This is optional.
  2727. * The callback to be called when an error occurred.
  2728. *
  2729. * @param {Object} config.failure.error
  2730. * The occurred error.
  2731. *
  2732. * @param {Object} config.scope
  2733. * The scope object
  2734. */
  2735. seek: function(config) {
  2736. if (config.offset == null) {
  2737. Ext.Logger.error('Ext.device.filesystem.FileEntry#seek: You must specify an `offset` in the file.');
  2738. return null;
  2739. }
  2740. this.offset = config.offset || 0;
  2741. if (config.success) {
  2742. config.success.call(config.scope || this);
  2743. }
  2744. },
  2745. /**
  2746. * Reads the data from the file starting at the file offset.
  2747. *
  2748. * @param {Object} config
  2749. * The object which contains the following config options:
  2750. *
  2751. * @param {Number} config.length This is optional.
  2752. * The length of bytes to read from the file. Defaults to the file's current size if unspecified.
  2753. *
  2754. * @param {String} config.encoding
  2755. * Optional encoding type used only for reading as Text
  2756. *
  2757. * @param {String} config.type
  2758. * Type of reading to use options are "text" (default), "dataURL", "binaryString" and "arrayBuffer"
  2759. *
  2760. * @param {Object} config.reader
  2761. * Optional config params to be applied to a File Reader
  2762. *
  2763. * @param {Function} config.reader.onloadstart
  2764. * @param {Function} config.reader.onloadprogress
  2765. * @param {Function} config.reader.onload
  2766. * @param {Function} config.reader.onabort
  2767. * @param {Function} config.reader.onerror
  2768. * @param {Function} config.reader.onloadend
  2769. *
  2770. * @param {Function} config.success This is optional.
  2771. * The callback to be called when the data has been successfully read.
  2772. *
  2773. * @param {Object} config.success.data
  2774. * The read data.
  2775. *
  2776. * @param {Function} config.failure This is optional.
  2777. * The callback to be called when an error occurred.
  2778. *
  2779. * @param {Object} config.failure.error
  2780. * The occurred error.
  2781. *
  2782. * @param {Object} config.scope
  2783. * The scope object
  2784. */
  2785. read: function(config) {
  2786. var me = this;
  2787. this.getEntry({
  2788. success: function(fileEntry) {
  2789. fileEntry.file(function(file) {
  2790. if (Ext.isNumber(config.length)) {
  2791. if (Ext.isFunction(file.slice)) {
  2792. file = file.slice(me.offset, config.length);
  2793. } else {
  2794. if (config.failure) {
  2795. config.failure.call(config.scope || me, {
  2796. code: -2,
  2797. message: "File missing slice functionality"
  2798. });
  2799. }
  2800. return;
  2801. }
  2802. }
  2803. var reader = new FileReader();
  2804. reader.onloadend = function(evt) {
  2805. config.success.call(config.scope || me, evt.target.result);
  2806. };
  2807. reader.onerror = function(error) {
  2808. config.failure.call(config.scope || me, error);
  2809. };
  2810. if (config.reader) {
  2811. reader = Ext.applyIf(reader, config.reader);
  2812. }
  2813. config.encoding = config.encoding || "UTF8";
  2814. switch (config.type) {
  2815. default:
  2816. case "text":
  2817. reader.readAsText(file, config.encoding);
  2818. break;
  2819. case "dataURL":
  2820. reader.readAsDataURL(file);
  2821. break;
  2822. case "binaryString":
  2823. reader.readAsBinaryString(file);
  2824. break;
  2825. case "arrayBuffer":
  2826. reader.readAsArrayBuffer(file);
  2827. break;
  2828. }
  2829. }, function(error) {
  2830. config.failure.call(config.scope || me, error);
  2831. });
  2832. },
  2833. failure: function(error) {
  2834. config.failure.call(config.scope || me, error);
  2835. }
  2836. });
  2837. },
  2838. /**
  2839. * Writes the data to the file starting at the file offset.
  2840. *
  2841. * @param {Object} config
  2842. * The object which contains the following config options:
  2843. *
  2844. * @param {Object} config.data This is required.
  2845. * The data to write to the file.
  2846. *
  2847. * @param {Boolean} config.append This is optional.
  2848. * Append to the end of the file
  2849. *
  2850. * @param {Object} config.writer
  2851. * Optional config params to be applied to a File Reader
  2852. *
  2853. * @param {Function} config.writer.onwritestart
  2854. * @param {Function} config.writer.onprogress
  2855. * @param {Function} config.writer.onwrite
  2856. * @param {Function} config.writer.onabort
  2857. * @param {Function} config.writer.onerror
  2858. * @param {Function} config.writer.onwriteend
  2859. *
  2860. * @param {Function} config.success This is optional.
  2861. * The callback to be called when the data has been successfully written.
  2862. *
  2863. * @param {Function} config.failure This is optional.
  2864. * The callback to be called when an error occurred.
  2865. *
  2866. * @param {Object} config.failure.error
  2867. * The occurred error.
  2868. *
  2869. * @param {Object} config.scope
  2870. * The scope object
  2871. */
  2872. write: function(config) {
  2873. if (config.data == null) {
  2874. Ext.Logger.error('Ext.device.filesystem.FileEntry#write: You must specify `data` to write into the file.');
  2875. return null;
  2876. }
  2877. var me = this;
  2878. this.getEntry({
  2879. options: config.options || {},
  2880. success: function(fileEntry) {
  2881. fileEntry.createWriter(function(writer) {
  2882. writer.onwriteend = function(evt) {
  2883. me.length = evt.target.length;
  2884. config.success.call(config.scope || me, evt.result);
  2885. };
  2886. writer.onerror = function(error) {
  2887. config.failure.call(config.scope || me, error);
  2888. };
  2889. if (config.writer) {
  2890. writer = Ext.applyIf(writer, config.writer);
  2891. }
  2892. if (me.offset) {
  2893. writer.seek(me.offset);
  2894. } else if (config.append) {
  2895. writer.seek(me.length);
  2896. }
  2897. me.writeData(writer, config.data);
  2898. }, function(error) {
  2899. config.failure.call(config.scope || me, error);
  2900. });
  2901. },
  2902. failure: function(error) {
  2903. config.failure.call(config.scope || me, error);
  2904. }
  2905. });
  2906. },
  2907. writeData: function(writer, data) {
  2908. writer.write(new Blob([
  2909. data
  2910. ]));
  2911. },
  2912. /**
  2913. * Truncates or extends the file to the specified size in bytes.
  2914. * If the file is extended, the added bytes are null bytes.
  2915. *
  2916. * @param {Object} config
  2917. * The object which contains the following config options:
  2918. *
  2919. * @param {Number} config.size This is required.
  2920. * The new file size.
  2921. *
  2922. * @param {Function} config.success This is optional.
  2923. * The callback to be called when the file size has been successfully changed.
  2924. *
  2925. * @param {Function} config.failure This is optional.
  2926. * The callback to be called when an error occurred.
  2927. *
  2928. * @param {Object} config.failure.error
  2929. * The occurred error.
  2930. *
  2931. * @param {Object} config.scope
  2932. * The scope object
  2933. */
  2934. truncate: function(config) {
  2935. if (config.size == null) {
  2936. Ext.Logger.error('Ext.device.filesystem.FileEntry#write: You must specify a `size` of the file.');
  2937. return null;
  2938. }
  2939. var me = this;
  2940. //noinspection JSValidateTypes
  2941. this.getEntry({
  2942. success: function(fileEntry) {
  2943. fileEntry.createWriter(function(writer) {
  2944. writer.truncate(config.size);
  2945. config.success.call(config.scope || me, me);
  2946. }, function(error) {
  2947. config.failure.call(config.scope || me, error);
  2948. });
  2949. },
  2950. failure: function(error) {
  2951. config.failure.call(config.scope || me, error);
  2952. }
  2953. });
  2954. }
  2955. });
  2956. });
  2957. });
  2958. /**
  2959. * Cordova File APi Abstraction
  2960. *
  2961. * For more documentation see
  2962. * http://docs.phonegap.com/en/2.7.0/cordova_file_file.md.html#File
  2963. */
  2964. Ext.define('Ext.device.filesystem.Cordova', {
  2965. alternateClassName: 'Ext.device.filesystem.PhoneGap',
  2966. extend: 'Ext.device.filesystem.HTML5',
  2967. constructor: function() {
  2968. Ext.override(Ext.device.filesystem.Entry, {
  2969. /**
  2970. *
  2971. * @param {Object} config
  2972. *
  2973. * @param {Object} config.metadata
  2974. * Metadata to add to the file or directory
  2975. *
  2976. * @param {Object} config.options
  2977. * File creation options {create:true, exclusive:false}
  2978. *
  2979. * @param {Boolean} config.options.create
  2980. * Indicates if the file should be created if it doesn't exist
  2981. *
  2982. * @param {Boolean} config.options.exclusive
  2983. * Used with the create option only indicates whether a creation causes an error if the file already exists
  2984. *
  2985. * @param {Function} config.success
  2986. * The function called when the File's Metadata is written successfully
  2987. *
  2988. * @param {Function} config.failure
  2989. * The function called when the File request causes an error
  2990. *
  2991. * @param {FileError} config.failure.error
  2992. *
  2993. */
  2994. writeMetadata: function(config) {
  2995. var me = this;
  2996. this.getEntry({
  2997. options: config.options,
  2998. success: function(entry) {
  2999. entry.setMetadata(function() {
  3000. config.success.call(config.scope || me);
  3001. }, function(error) {
  3002. config.failure.call(config.scope || me, error);
  3003. }, config.metadata);
  3004. },
  3005. failure: function(error) {
  3006. config.failure.call(config.scope || me, error);
  3007. }
  3008. });
  3009. },
  3010. /**
  3011. *
  3012. * @param {Object} config
  3013. *
  3014. * @param {Object} config.options
  3015. * File creation options {create:true, exclusive:false}
  3016. *
  3017. * @param {Boolean} config.options.create
  3018. * Indicates if the file should be created if it doesn't exist
  3019. *
  3020. * @param {Boolean} config.options.exclusive
  3021. * Used with the create option only indicates whether a creation causes an error if the file already exists
  3022. *
  3023. * @param {Function} config.success
  3024. * The function called when the File's Metadata is written successfully
  3025. *
  3026. * @param {Function} config.failure
  3027. * The function called when the File request causes an error
  3028. *
  3029. * @param {FileError} config.failure.error
  3030. *
  3031. */
  3032. readMetadata: function(config) {
  3033. var me = this;
  3034. this.getEntry({
  3035. options: config.options,
  3036. success: function(entry) {
  3037. entry.getMetadata(function(metadata) {
  3038. config.success.call(config.scope || me, metadata);
  3039. }, function(error) {
  3040. config.failure.call(config.scope || me, error);
  3041. });
  3042. },
  3043. failure: function(error) {
  3044. config.failure.call(config.scope || me, error);
  3045. }
  3046. });
  3047. }
  3048. });
  3049. Ext.override(Ext.device.filesystem.FileEntry, {
  3050. writeData: function(writer, data) {
  3051. writer.write(data.toString());
  3052. },
  3053. /**
  3054. * Send a file to a server
  3055. *
  3056. * @param {Object} config
  3057. *
  3058. * @param {String} config.url
  3059. * URL of server to receive the file
  3060. *
  3061. * @param {Boolean} config.trustAllHosts
  3062. * (Optional) If true it will accept all security certificates. Defaults to false
  3063. *
  3064. * @param {String} config.fileKey
  3065. * Name of the form element. Defaults to "file"
  3066. *
  3067. * @param {String} config.fileName
  3068. * Name of the file on the server
  3069. *
  3070. * @param {String} config.mimeType
  3071. * mime type of the data being uploaded. defaults to "image/jpeg"
  3072. *
  3073. * @param {Object} config.params
  3074. * (Optional) set of key/value pairs to be passed along with the request
  3075. *
  3076. * @param {Boolean} config.chunkMode
  3077. * Should the data be uploaded in a chunked streaming mode. defaults to true
  3078. *
  3079. * @param {Object} config.headers
  3080. * Map of header name => header values. Multiple values should be specified an array of values
  3081. * var headers={'headerParam':'headerValue'};
  3082. *
  3083. * @param {Function} config.success
  3084. * The function called when the File is uploaded successfully
  3085. *
  3086. * @param {Function} config.success.metadata
  3087. *
  3088. * @param {Function} config.failure
  3089. * The function called when the File upload fails
  3090. *
  3091. * @param {FileError} config.failure.error
  3092. *
  3093. * @return {FileTransfer}
  3094. */
  3095. upload: function(config) {
  3096. var options = new FileUploadOptions();
  3097. options.fileKey = config.fileKey || "file";
  3098. options.fileName = this.path.substr(this.path.lastIndexOf('/') + 1);
  3099. options.mimeType = config.mimeType || "image/jpeg";
  3100. options.params = config.params || {};
  3101. options.headers = config.headers || {};
  3102. options.chunkMode = config.chunkMode || true;
  3103. var fileTransfer = new FileTransfer();
  3104. fileTransfer.upload(this.path, encodeURI(config.url), config.success, config.failure, options, config.trustAllHosts || false);
  3105. return fileTransfer;
  3106. },
  3107. /**
  3108. * Downloads a file from the server saving it into the Local File System
  3109. *
  3110. * @param {Object} config
  3111. *
  3112. * @param {String} config.source
  3113. * URL of file to download
  3114. *
  3115. * @param {Boolean} config.trustAllHosts
  3116. * if true it will accept all security certificates. Defaults to false
  3117. *
  3118. * @param {Object} config.options
  3119. * Header parameters (Auth, etc)
  3120. *
  3121. * {
  3122. * headers: {
  3123. * "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
  3124. * }
  3125. * }
  3126. *
  3127. * @param {Function} config.success
  3128. * The function called when the File is downloaded successfully
  3129. *
  3130. * @param {Function} config.success.entry
  3131. * File Entry object of the downloaded file
  3132. *
  3133. * @param {Function} config.failure
  3134. * The function called when the File download fails
  3135. *
  3136. * @param {FileError} config.failure.error
  3137. *
  3138. * @return {FileTransfer}
  3139. */
  3140. download: function(config) {
  3141. var fileTransfer = new FileTransfer();
  3142. fileTransfer.download(encodeURI(config.source), this.path, config.success, config.failure, config.trustAllHosts || false, config.options || {});
  3143. return fileTransfer;
  3144. }
  3145. });
  3146. }
  3147. });
  3148. /**
  3149. * @private
  3150. */
  3151. Ext.define('Ext.device.filesystem.Chrome', {
  3152. extend: 'Ext.device.filesystem.HTML5',
  3153. /**
  3154. * Requests access to the Local File System
  3155. *
  3156. * var me = this;
  3157. * var fs = Ext.create("Ext.device.File", {});
  3158. * fs.requestFileSystem({
  3159. * type: window.PERSISTENT,
  3160. * size: 1024 * 1024,
  3161. * success: function(fileSystem) {
  3162. * me.fs = fileSystem;
  3163. * },
  3164. * failure: function(err) {
  3165. * console.log("FileSystem Failure: " + err.code);
  3166. * }
  3167. * });
  3168. *
  3169. *
  3170. * @param {Object} config An object which contains the follow options
  3171. * @param {Number} config.type
  3172. * window.TEMPORARY (0) or window.PERSISTENT (1)
  3173. *
  3174. * @param {Number} config.size
  3175. * Storage space, in Bytes, needed by the application
  3176. *
  3177. * @param {Function} config.success
  3178. * The function called when the filesystem is returned successfully
  3179. *
  3180. * @param {FileSystem} config.success.fs
  3181. *
  3182. * @param {Function} config.failure
  3183. * The function called when the filesystem request causes and error
  3184. *
  3185. * @param {FileError} config.failure.error
  3186. *
  3187. */
  3188. requestFileSystem: function(config) {
  3189. var me = this;
  3190. config = Ext.device.filesystem.Abstract.prototype.requestFileSystem(config);
  3191. var successCallback = function(fs) {
  3192. var fileSystem = Ext.create('Ext.device.filesystem.FileSystem', fs);
  3193. config.success.call(config.scope || me, fileSystem);
  3194. };
  3195. if (config.type == window.PERSISTENT) {
  3196. if (navigator.webkitPersistentStorage) {
  3197. navigator.webkitPersistentStorage.requestQuota(config.size, function(grantedBytes) {
  3198. window.webkitRequestFileSystem(config.type, grantedBytes, successCallback, config.failure);
  3199. });
  3200. } else {
  3201. window.webkitStorageInfo.requestQuota(window.PERSISTENT, config.size, function(grantedBytes) {
  3202. window.webkitRequestFileSystem(config.type, grantedBytes, successCallback, config.failure);
  3203. });
  3204. }
  3205. } else {
  3206. window.webkitRequestFileSystem(config.type, config.size, successCallback, config.failure);
  3207. }
  3208. }
  3209. });
  3210. /**
  3211. * @private
  3212. */
  3213. Ext.define('Ext.device.filesystem.Simulator', {
  3214. extend: 'Ext.device.filesystem.HTML5'
  3215. });
  3216. /**
  3217. * Provides an API to navigate file system hierarchies.
  3218. *
  3219. * @mixins Ext.device.filesystem.Sencha
  3220. */
  3221. Ext.define('Ext.device.FileSystem', {
  3222. singleton: true,
  3223. requires: [
  3224. 'Ext.device.Communicator',
  3225. 'Ext.device.filesystem.Cordova',
  3226. 'Ext.device.filesystem.Chrome',
  3227. 'Ext.device.filesystem.Simulator'
  3228. ],
  3229. constructor: function() {
  3230. var browserEnv = Ext.browser.is;
  3231. if (browserEnv.WebView) {
  3232. if (browserEnv.Cordova) {
  3233. return Ext.create('Ext.device.filesystem.Cordova');
  3234. }
  3235. } else if (browserEnv.Chrome) {
  3236. return Ext.create('Ext.device.filesystem.Chrome');
  3237. }
  3238. return Ext.create('Ext.device.filesystem.Simulator');
  3239. }
  3240. });
  3241. /**
  3242. * @private
  3243. */
  3244. Ext.define('Ext.device.geolocation.Abstract', {
  3245. config: {
  3246. /**
  3247. * @cfg {Number} maximumAge
  3248. * This option indicates that the application is willing to accept cached location information whose age
  3249. * is no greater than the specified time in milliseconds. If maximumAge is set to 0, an attempt to retrieve
  3250. * new location information is made immediately.
  3251. */
  3252. maximumAge: 0,
  3253. /**
  3254. * @cfg {Number} frequency The default frequency to get the current position when using {@link Ext.device.Geolocation#watchPosition}.
  3255. */
  3256. frequency: 10000,
  3257. /**
  3258. * @cfg {Boolean} allowHighAccuracy True to allow high accuracy when getting the current position.
  3259. */
  3260. allowHighAccuracy: false,
  3261. /**
  3262. * @cfg {Number} timeout
  3263. * The maximum number of milliseconds allowed to elapse between a location update operation.
  3264. */
  3265. timeout: Infinity
  3266. },
  3267. /**
  3268. * Attempts to get the current position of this device.
  3269. *
  3270. * Ext.device.Geolocation.getCurrentPosition({
  3271. * success: function(position) {
  3272. * console.log(position);
  3273. * },
  3274. * failure: function() {
  3275. * Ext.Msg.alert('Geolocation', 'Something went wrong!');
  3276. * }
  3277. * });
  3278. *
  3279. * *Note:* If you want to watch the current position, you could use {@link Ext.device.Geolocation#watchPosition} instead.
  3280. *
  3281. * @param {Object} config An object which contains the following config options:
  3282. *
  3283. * @param {Function} config.success
  3284. * The function to call when the location of the current device has been received.
  3285. *
  3286. * @param {Object} config.success.position
  3287. *
  3288. * @param {Function} config.failure
  3289. * The function that is called when something goes wrong.
  3290. *
  3291. * @param {Object} config.scope
  3292. * The scope of the `success` and `failure` functions.
  3293. *
  3294. * @param {Number} config.maximumAge
  3295. * The maximum age of a cached location. If you do not enter a value for this, the value of {@link #maximumAge}
  3296. * will be used.
  3297. *
  3298. * @param {Number} config.timeout
  3299. * The timeout for this request. If you do not specify a value, it will default to {@link #timeout}.
  3300. *
  3301. * @param {Boolean} config.allowHighAccuracy
  3302. * True to enable allow accuracy detection of the location of the current device. If you do not specify a value, it will
  3303. * default to {@link #allowHighAccuracy}.
  3304. */
  3305. getCurrentPosition: function(config) {
  3306. var defaultConfig = Ext.device.geolocation.Abstract.prototype.config;
  3307. config = Ext.applyIf(config, {
  3308. maximumAge: defaultConfig.maximumAge,
  3309. frequency: defaultConfig.frequency,
  3310. allowHighAccuracy: defaultConfig.allowHighAccuracy,
  3311. timeout: defaultConfig.timeout
  3312. });
  3313. // <debug>
  3314. if (!config.success) {
  3315. Ext.Logger.warn('You need to specify a `success` function for #getCurrentPosition');
  3316. }
  3317. // </debug>
  3318. return config;
  3319. },
  3320. /**
  3321. * Watches for the current position and calls the callback when successful depending on the specified {@link #frequency}.
  3322. *
  3323. * Ext.device.Geolocation.watchPosition({
  3324. * callback: function(position) {
  3325. * console.log(position);
  3326. * },
  3327. * failure: function() {
  3328. * Ext.Msg.alert('Geolocation', 'Something went wrong!');
  3329. * }
  3330. * });
  3331. *
  3332. * @param {Object} config An object which contains the following config options:
  3333. *
  3334. * @param {Function} config.callback
  3335. * The function to be called when the position has been updated.
  3336. *
  3337. * @param {Function} config.failure
  3338. * The function that is called when something goes wrong.
  3339. *
  3340. * @param {Object} config.scope
  3341. * The scope of the `success` and `failure` functions.
  3342. *
  3343. * @param {Boolean} config.frequency
  3344. * The frequency in which to call the supplied callback. Defaults to {@link #frequency} if you do not specify a value.
  3345. *
  3346. * @param {Boolean} config.allowHighAccuracy
  3347. * True to enable allow accuracy detection of the location of the current device. If you do not specify a value, it will
  3348. * default to {@link #allowHighAccuracy}.
  3349. */
  3350. watchPosition: function(config) {
  3351. var defaultConfig = Ext.device.geolocation.Abstract.prototype.config;
  3352. config = Ext.applyIf(config, {
  3353. maximumAge: defaultConfig.maximumAge,
  3354. frequency: defaultConfig.frequency,
  3355. allowHighAccuracy: defaultConfig.allowHighAccuracy,
  3356. timeout: defaultConfig.timeout
  3357. });
  3358. // <debug>
  3359. if (!config.callback) {
  3360. Ext.Logger.warn('You need to specify a `callback` function for #watchPosition');
  3361. }
  3362. // </debug>
  3363. return config;
  3364. },
  3365. /**
  3366. * If you are currently watching for the current position, this will stop that task.
  3367. */
  3368. clearWatch: function() {}
  3369. });
  3370. /**
  3371. * @private
  3372. */
  3373. Ext.define('Ext.device.geolocation.Cordova', {
  3374. alternateClassName: 'Ext.device.geolocation.PhoneGap',
  3375. extend: 'Ext.device.geolocation.Abstract',
  3376. activeWatchID: null,
  3377. getCurrentPosition: function(config) {
  3378. config = this.callParent(arguments);
  3379. navigator.geolocation.getCurrentPosition(config.success, config.failure, config);
  3380. return config;
  3381. },
  3382. watchPosition: function(config) {
  3383. config = this.callParent(arguments);
  3384. if (this.activeWatchID) {
  3385. this.clearWatch();
  3386. }
  3387. this.activeWatchID = navigator.geolocation.watchPosition(config.callback, config.failure, config);
  3388. return config;
  3389. },
  3390. clearWatch: function() {
  3391. if (this.activeWatchID) {
  3392. navigator.geolocation.clearWatch(this.activeWatchID);
  3393. this.activeWatchID = null;
  3394. }
  3395. }
  3396. });
  3397. /**
  3398. * @private
  3399. */
  3400. Ext.define('Ext.device.geolocation.Simulator', {
  3401. extend: 'Ext.device.geolocation.Abstract',
  3402. requires: [
  3403. 'Ext.util.Geolocation'
  3404. ],
  3405. getCurrentPosition: function(config) {
  3406. config = this.callParent([
  3407. config
  3408. ]);
  3409. Ext.apply(config, {
  3410. autoUpdate: false,
  3411. listeners: {
  3412. scope: this,
  3413. locationupdate: function(geolocation) {
  3414. if (config.success) {
  3415. config.success.call(config.scope || this, geolocation.position);
  3416. }
  3417. },
  3418. locationerror: function() {
  3419. if (config.failure) {
  3420. config.failure.call(config.scope || this);
  3421. }
  3422. }
  3423. }
  3424. });
  3425. this.geolocation = Ext.create('Ext.util.Geolocation', config);
  3426. this.geolocation.updateLocation();
  3427. return config;
  3428. },
  3429. watchPosition: function(config) {
  3430. config = this.callParent([
  3431. config
  3432. ]);
  3433. Ext.apply(config, {
  3434. listeners: {
  3435. scope: this,
  3436. locationupdate: function(geolocation) {
  3437. if (config.callback) {
  3438. config.callback.call(config.scope || this, geolocation.position);
  3439. }
  3440. },
  3441. locationerror: function() {
  3442. if (config.failure) {
  3443. config.failure.call(config.scope || this);
  3444. }
  3445. }
  3446. }
  3447. });
  3448. this.geolocation = Ext.create('Ext.util.Geolocation', config);
  3449. return config;
  3450. },
  3451. clearWatch: function() {
  3452. if (this.geolocation) {
  3453. this.geolocation.destroy();
  3454. }
  3455. this.geolocation = null;
  3456. }
  3457. });
  3458. /**
  3459. * Provides access to the native Geolocation API when running on a device. There are three implementations of this API:
  3460. *
  3461. * - Sencha Packager
  3462. * - [PhoneGap](http://docs.phonegap.com/en/1.4.1/phonegap_device_device.md.html)
  3463. * - Browser
  3464. *
  3465. * This class will automatically select the correct implementation depending on the device your application is running on.
  3466. *
  3467. * ## Examples
  3468. *
  3469. * Getting the current location:
  3470. *
  3471. * Ext.device.Geolocation.getCurrentPosition({
  3472. * success: function(position) {
  3473. * console.log(position.coords);
  3474. * },
  3475. * failure: function() {
  3476. * console.log('something went wrong!');
  3477. * }
  3478. * });
  3479. *
  3480. * Watching the current location:
  3481. *
  3482. * Ext.device.Geolocation.watchPosition({
  3483. * frequency: 3000, // Update every 3 seconds
  3484. * callback: function(position) {
  3485. * console.log('Position updated!', position.coords);
  3486. * },
  3487. * failure: function() {
  3488. * console.log('something went wrong!');
  3489. * }
  3490. * });
  3491. *
  3492. * @mixins Ext.device.geolocation.Abstract
  3493. */
  3494. Ext.define('Ext.device.Geolocation', {
  3495. singleton: true,
  3496. requires: [
  3497. 'Ext.device.Communicator',
  3498. 'Ext.device.geolocation.Cordova',
  3499. 'Ext.device.geolocation.Simulator'
  3500. ],
  3501. constructor: function() {
  3502. var browserEnv = Ext.browser.is;
  3503. if (browserEnv.WebView) {
  3504. if (browserEnv.Cordova) {
  3505. return Ext.create('Ext.device.geolocation.Cordova');
  3506. }
  3507. }
  3508. return Ext.create('Ext.device.geolocation.Simulator');
  3509. }
  3510. });
  3511. /**
  3512. * @private
  3513. */
  3514. Ext.define('Ext.device.globalization.Abstract', {
  3515. mixins: [
  3516. 'Ext.mixin.Observable'
  3517. ],
  3518. config: {
  3519. formatLength: 'full',
  3520. selector: 'date and time',
  3521. dateType: 'wide',
  3522. items: 'months',
  3523. numberType: 'decimal',
  3524. currencyCode: "USD"
  3525. },
  3526. getPreferredLanguage: function(config) {
  3527. // <debug>
  3528. if (!config.success) {
  3529. Ext.Logger.warn('You need to specify a `success` function for #getPreferredLanguage');
  3530. }
  3531. // </debug>
  3532. return config;
  3533. },
  3534. getLocaleName: function(config) {
  3535. // <debug>
  3536. if (!config.success) {
  3537. Ext.Logger.warn('You need to specify a `success` function for #getLocaleName');
  3538. }
  3539. // </debug>
  3540. return config;
  3541. },
  3542. dateToString: function(config) {
  3543. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3544. config = Ext.applyIf(config, {
  3545. date: new Date(),
  3546. formatLength: defaultConfig.formatLength,
  3547. selector: defaultConfig.selector
  3548. });
  3549. // <debug>
  3550. if (!config.success) {
  3551. Ext.Logger.warn('You need to specify a `success` function for #dateToString');
  3552. }
  3553. // </debug>
  3554. return config;
  3555. },
  3556. stringToDate: function(config) {
  3557. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3558. config = Ext.applyIf(config, {
  3559. dateString: Ext.util.Format.date(new Date(), 'm/d/Y'),
  3560. formatLength: defaultConfig.formatLength,
  3561. selector: defaultConfig.selector
  3562. });
  3563. // <debug>
  3564. if (!config.success) {
  3565. Ext.Logger.warn('You need to specify a `success` function for #stringToDate');
  3566. }
  3567. // </debug>
  3568. return config;
  3569. },
  3570. getDatePattern: function(config) {
  3571. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3572. config = Ext.applyIf(config, {
  3573. formatLength: defaultConfig.formatLength,
  3574. selector: defaultConfig.selector
  3575. });
  3576. // <debug>
  3577. if (!config.success) {
  3578. Ext.Logger.warn('You need to specify a `success` function for #getDatePattern');
  3579. }
  3580. // </debug>
  3581. return config;
  3582. },
  3583. getDateNames: function(config) {
  3584. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3585. config = Ext.applyIf(config, {
  3586. type: defaultConfig.dateType,
  3587. items: defaultConfig.items
  3588. });
  3589. // <debug>
  3590. if (!config.success) {
  3591. Ext.Logger.warn('You need to specify a `success` function for #getDateNames');
  3592. }
  3593. // </debug>
  3594. return config;
  3595. },
  3596. isDayLightSavingsTime: function(config) {
  3597. config = Ext.applyIf(config, {
  3598. date: new Date()
  3599. });
  3600. // <debug>
  3601. if (!config.success) {
  3602. Ext.Logger.warn('You need to specify a `success` function for #isDayLightSavingsTime');
  3603. }
  3604. // </debug>
  3605. return config;
  3606. },
  3607. getFirstDayOfWeek: function(config) {
  3608. // <debug>
  3609. if (!config.success) {
  3610. Ext.Logger.warn('You need to specify a `success` function for #getFirstDayOfWeek');
  3611. }
  3612. // </debug>
  3613. return config;
  3614. },
  3615. numberToString: function(config) {
  3616. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3617. config = Ext.applyIf(config, {
  3618. number: defaultConfig.number,
  3619. type: defaultConfig.numberType
  3620. });
  3621. // <debug>
  3622. if (!config.number) {
  3623. Ext.Logger.warn('You need to specify a `number` for #numberToString');
  3624. }
  3625. if (!config.success) {
  3626. Ext.Logger.warn('You need to specify a `success` function for #numberToString');
  3627. }
  3628. // </debug>
  3629. return config;
  3630. },
  3631. stringToNumber: function(config) {
  3632. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3633. config = Ext.applyIf(config, {
  3634. type: defaultConfig.numberType
  3635. });
  3636. // <debug>
  3637. if (!config.number) {
  3638. Ext.Logger.warn('You need to specify a `string` for #stringToNumber');
  3639. }
  3640. if (!config.success) {
  3641. Ext.Logger.warn('You need to specify a `success` function for #stringToNumber');
  3642. }
  3643. // </debug>
  3644. return config;
  3645. },
  3646. getNumberPattern: function(config) {
  3647. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3648. config = Ext.applyIf(config, {
  3649. type: defaultConfig.numberType
  3650. });
  3651. if (!config.success) {
  3652. Ext.Logger.warn('You need to specify a `success` function for #getNumberPattern');
  3653. }
  3654. // </debug>
  3655. return config;
  3656. },
  3657. getCurrencyPattern: function(config) {
  3658. var defaultConfig = Ext.device.globalization.Abstract.prototype.config;
  3659. config = Ext.applyIf(config, {
  3660. currencyCode: defaultConfig.currencyCode
  3661. });
  3662. if (!config.success) {
  3663. Ext.Logger.warn('You need to specify a `success` function for #getCurrency');
  3664. }
  3665. // </debug>
  3666. return config;
  3667. }
  3668. });
  3669. /**
  3670. * @private
  3671. */
  3672. Ext.define('Ext.device.globalization.Cordova', {
  3673. alternateClassName: 'Ext.device.globalization.PhoneGap',
  3674. extend: 'Ext.device.globalization.Abstract',
  3675. getPreferredLanguage: function(config) {
  3676. config = this.callParent(arguments);
  3677. navigator.globalization.getPreferredLanguage(config.success, config.error);
  3678. },
  3679. getLocaleName: function(config) {
  3680. config = this.callParent(arguments);
  3681. navigator.globalization.getLocaleName(config.success, config.error);
  3682. },
  3683. dateToString: function(config) {
  3684. config = this.callParent(arguments);
  3685. navigator.globalization.dateToString(config.date, config.success, config.error, config);
  3686. },
  3687. stringToDate: function(config) {
  3688. config = this.callParent(arguments);
  3689. navigator.globalization.stringToDate(config.dateString, config.success, config.error, config);
  3690. },
  3691. getDatePattern: function(config) {
  3692. config = this.callParent(arguments);
  3693. navigator.globalization.getDatePattern(config.success, config.error, config);
  3694. },
  3695. getDateNames: function(config) {
  3696. config = this.callParent(arguments);
  3697. navigator.globalization.getDateNames(config.success, config.error, config);
  3698. },
  3699. isDayLightSavingsTime: function(config) {
  3700. config = this.callParent(arguments);
  3701. navigator.globalization.isDayLightSavingsTime(config.date, config.success, config.error, config);
  3702. },
  3703. getFirstDayOfWeek: function(config) {
  3704. config = this.callParent(arguments);
  3705. navigator.globalization.getFirstDayOfWeek(config.success, config.error);
  3706. },
  3707. numberToString: function(config) {
  3708. config = this.callParent(arguments);
  3709. navigator.globalization.numberToString(config.number, config.success, config.error, config);
  3710. },
  3711. stringToNumber: function(config) {
  3712. config = this.callParent(arguments);
  3713. navigator.globalization.stringToNumber(config.string, config.success, config.error, config);
  3714. },
  3715. getNumberPattern: function(config) {
  3716. config = this.callParent(arguments);
  3717. navigator.globalization.getNumberPattern(config.success, config.error, config);
  3718. },
  3719. getCurrencyPattern: function(config) {
  3720. config = this.callParent(arguments);
  3721. navigator.globalization.getCurrencyPattern(config.currencyCode, config.success, config.error);
  3722. }
  3723. });
  3724. /**
  3725. * @private
  3726. */
  3727. Ext.define('Ext.device.globalization.Simulator', {
  3728. extend: 'Ext.device.globalization.Abstract'
  3729. });
  3730. /**
  3731. * Provides access to the native Globalization API
  3732. *
  3733. * - [PhoneGap](http://docs.phonegap.com/en/2.6.0/cordova_globalization_globalization.md.html)
  3734. *
  3735. * Class currently only works with Cordova and does not have a simulated HTML counter part.
  3736. * Please see notes on Cordova Docs for more information.
  3737. *
  3738. * http://docs.phonegap.com/en/2.6.0/cordova_globalization_globalization.md.html
  3739. */
  3740. Ext.define('Ext.device.Globalization', {
  3741. singleton: true,
  3742. requires: [
  3743. 'Ext.device.globalization.Cordova',
  3744. 'Ext.device.globalization.Simulator'
  3745. ],
  3746. constructor: function() {
  3747. var browserEnv = Ext.browser.is;
  3748. if (browserEnv.WebView) {
  3749. if (browserEnv.Cordova) {
  3750. return Ext.create('Ext.device.globalization.Cordova');
  3751. }
  3752. }
  3753. return Ext.create('Ext.device.globalization.Simulator');
  3754. }
  3755. });
  3756. /**
  3757. * @private
  3758. */
  3759. Ext.define('Ext.device.media.Abstract', {
  3760. mixins: [
  3761. 'Ext.mixin.Observable'
  3762. ],
  3763. config: {
  3764. src: null
  3765. },
  3766. play: Ext.emptyFn,
  3767. pause: Ext.emptyFn,
  3768. stop: Ext.emptyFn,
  3769. release: Ext.emptyFn,
  3770. seekTo: Ext.emptyFn,
  3771. getCurrentPosition: Ext.emptyFn,
  3772. getDuration: Ext.emptyFn,
  3773. startRecord: Ext.emptyFn,
  3774. stopRecord: Ext.emptyFn
  3775. });
  3776. /**
  3777. * @private
  3778. */
  3779. Ext.define('Ext.device.media.Cordova', {
  3780. alternateClassName: 'Ext.device.media.PhoneGap',
  3781. extend: 'Ext.device.media.Abstract',
  3782. config: {
  3783. /**
  3784. * A URI containing the audio content.
  3785. * @type {String}
  3786. */
  3787. src: null,
  3788. /**
  3789. * @private
  3790. */
  3791. media: null
  3792. },
  3793. updateSrc: function(newSrc, oldSrc) {
  3794. this.setMedia(new Media(newSrc));
  3795. },
  3796. play: function() {
  3797. var media = this.getMedia();
  3798. if (media) {
  3799. media.play();
  3800. }
  3801. },
  3802. pause: function() {
  3803. var media = this.getMedia();
  3804. if (media) {
  3805. media.pause();
  3806. }
  3807. },
  3808. stop: function() {
  3809. var media = this.getMedia();
  3810. if (media) {
  3811. media.stop();
  3812. }
  3813. },
  3814. release: function() {
  3815. var media = this.getMedia();
  3816. if (media) {
  3817. media.release();
  3818. }
  3819. },
  3820. seekTo: function(miliseconds) {
  3821. var media = this.getMedia();
  3822. if (media) {
  3823. media.seekTo(miliseconds);
  3824. }
  3825. },
  3826. getDuration: function() {
  3827. var media = this.getMedia();
  3828. if (media) {
  3829. media.getDuration();
  3830. }
  3831. },
  3832. startRecord: function() {
  3833. var media = this.getMedia();
  3834. if (!media) {
  3835. this.setSrc(null);
  3836. }
  3837. media.startRecord();
  3838. },
  3839. stopRecord: function() {
  3840. var media = this.getMedia();
  3841. if (media) {
  3842. media.stopRecord();
  3843. }
  3844. }
  3845. });
  3846. /**
  3847. * @mixins Ext.device.media.Abstract
  3848. */
  3849. Ext.define('Ext.device.Media', {
  3850. singleton: true,
  3851. requires: [
  3852. 'Ext.device.Communicator',
  3853. 'Ext.device.media.Cordova'
  3854. ],
  3855. constructor: function() {
  3856. var browserEnv = Ext.browser.is;
  3857. if (browserEnv.WebView && browserEnv.Cordova) {
  3858. return Ext.create('Ext.device.media.Cordova');
  3859. }
  3860. return Ext.create('Ext.device.media.Abstract');
  3861. }
  3862. });
  3863. /**
  3864. * @private
  3865. */
  3866. Ext.define('Ext.device.notification.Abstract', {
  3867. /**
  3868. * A simple way to show a notification.
  3869. *
  3870. * Ext.device.Notification.show({
  3871. * title: 'Verification',
  3872. * message: 'Is your email address is: test@sencha.com',
  3873. * buttons: Ext.MessageBox.OKCANCEL,
  3874. * callback: function(button) {
  3875. * if (button == "ok") {
  3876. * console.log('Verified');
  3877. * } else {
  3878. * console.log('Nope.');
  3879. * }
  3880. * }
  3881. * });
  3882. *
  3883. * @param {Object} config An object which contains the following config options:
  3884. *
  3885. * @param {String} config.title The title of the notification
  3886. *
  3887. * @param {String} config.message The message to be displayed on the notification
  3888. *
  3889. * @param {String/String[]} [config.buttons="OK"]
  3890. * The buttons to be displayed on the notification. It can be a string, which is the title of the button, or an array of multiple strings.
  3891. * Please not that you should not use more than 2 buttons, as they may not be displayed correct on all devices.
  3892. *
  3893. * @param {Function} config.callback
  3894. * A callback function which is called when the notification is dismissed by clicking on the configured buttons.
  3895. * @param {String} config.callback.buttonId The id of the button pressed, one of: 'ok', 'yes', 'no', 'cancel'.
  3896. *
  3897. * @param {Object} config.scope The scope of the callback function
  3898. */
  3899. show: function(config) {
  3900. if (!config.message) {
  3901. throw ('[Ext.device.Notification#show] You passed no message');
  3902. }
  3903. if (!config.buttons) {
  3904. config.buttons = [
  3905. "OK",
  3906. "Cancel"
  3907. ];
  3908. }
  3909. if (!Ext.isArray(config.buttons)) {
  3910. config.buttons = [
  3911. config.buttons
  3912. ];
  3913. }
  3914. if (!config.scope) {
  3915. config.scope = this;
  3916. }
  3917. return config;
  3918. },
  3919. alert: function(config) {
  3920. if (!config.message) {
  3921. throw ('[Ext.device.Notification#alert] You passed no message');
  3922. }
  3923. if (!config.scope) {
  3924. config.scope = this;
  3925. }
  3926. return config;
  3927. },
  3928. confirm: function(config) {
  3929. if (!config.message) {
  3930. throw ('[Ext.device.Notification#confirm] You passed no message');
  3931. }
  3932. if (!config.buttons) {
  3933. config.buttons = [
  3934. "OK",
  3935. "Cancel"
  3936. ];
  3937. }
  3938. if (!Ext.isArray(config.buttons)) {
  3939. config.buttons = [
  3940. config.buttons
  3941. ];
  3942. }
  3943. if (!config.scope) {
  3944. config.scope = this;
  3945. }
  3946. return config;
  3947. },
  3948. prompt: function(config) {
  3949. if (!config.message) {
  3950. throw ('[Ext.device.Notification#prompt] You passed no message');
  3951. }
  3952. if (!config.buttons) {
  3953. config.buttons = [
  3954. "OK",
  3955. "Cancel"
  3956. ];
  3957. }
  3958. if (!Ext.isArray(config.buttons)) {
  3959. config.buttons = [
  3960. config.buttons
  3961. ];
  3962. }
  3963. if (!config.scope) {
  3964. config.scope = this;
  3965. }
  3966. return config;
  3967. },
  3968. /**
  3969. * Vibrates the device.
  3970. */
  3971. vibrate: Ext.emptyFn,
  3972. beep: Ext.emptyFn
  3973. });
  3974. /**
  3975. * @private
  3976. */
  3977. Ext.define('Ext.device.notification.Cordova', {
  3978. alternateClassName: 'Ext.device.notification.PhoneGap',
  3979. extend: 'Ext.device.notification.Abstract',
  3980. requires: [
  3981. 'Ext.device.Communicator'
  3982. ],
  3983. show: function(config) {
  3984. config = this.callParent(arguments);
  3985. this.confirm(config);
  3986. },
  3987. confirm: function(config) {
  3988. config = this.callParent(arguments);
  3989. var buttons = config.buttons,
  3990. ln = config.buttons.length;
  3991. if (ln && typeof buttons[0] != "string") {
  3992. var newButtons = [],
  3993. i;
  3994. for (i = 0; i < ln; i++) {
  3995. newButtons.push(buttons[i].text);
  3996. }
  3997. buttons = newButtons;
  3998. }
  3999. var callback = function(index) {
  4000. if (config.callback) {
  4001. config.callback.apply(config.scope, (buttons) ? [
  4002. buttons[index - 1].toLowerCase()
  4003. ] : []);
  4004. }
  4005. };
  4006. navigator.notification.confirm(config.message, callback, config.title, buttons);
  4007. },
  4008. alert: function(config) {
  4009. navigator.notification.alert(config.message, config.callback, config.title, config.buttonName);
  4010. },
  4011. prompt: function(config) {
  4012. config = this.callParent(arguments);
  4013. var buttons = config.buttons,
  4014. ln = config.buttons.length;
  4015. if (ln && typeof buttons[0] != "string") {
  4016. var newButtons = [],
  4017. i;
  4018. for (i = 0; i < ln; i++) {
  4019. newButtons.push(buttons[i].text);
  4020. }
  4021. buttons = newButtons;
  4022. }
  4023. var callback = function(result) {
  4024. if (config.callback) {
  4025. config.callback.call(config.scope, (buttons) ? buttons[result.buttonIndex - 1].toLowerCase() : null, result.input1);
  4026. }
  4027. };
  4028. navigator.notification.prompt(config.message, callback, config.title, buttons);
  4029. },
  4030. vibrate: function(time) {
  4031. navigator.notification.vibrate(time);
  4032. },
  4033. beep: function(times) {
  4034. navigator.notification.vibrate(times);
  4035. }
  4036. });
  4037. /**
  4038. * @private
  4039. */
  4040. Ext.define('Ext.device.notification.Simulator', {
  4041. extend: 'Ext.device.notification.Abstract',
  4042. requires: [
  4043. 'Ext.MessageBox',
  4044. 'Ext.util.Audio'
  4045. ],
  4046. /**
  4047. * @private
  4048. */
  4049. msg: null,
  4050. show: function() {
  4051. var config = this.callParent(arguments),
  4052. buttons = [],
  4053. ln = config.buttons.length,
  4054. button, i, callback;
  4055. //buttons
  4056. for (i = 0; i < ln; i++) {
  4057. button = config.buttons[i];
  4058. if (Ext.isString(button)) {
  4059. button = {
  4060. text: config.buttons[i],
  4061. itemId: config.buttons[i].toLowerCase()
  4062. };
  4063. }
  4064. buttons.push(button);
  4065. }
  4066. this.msg = Ext.create('Ext.MessageBox');
  4067. callback = function(itemId) {
  4068. if (config.callback) {
  4069. config.callback.apply(config.scope, [
  4070. itemId
  4071. ]);
  4072. }
  4073. };
  4074. this.msg.show({
  4075. title: config.title,
  4076. message: config.message,
  4077. scope: this.msg,
  4078. buttons: buttons,
  4079. fn: callback
  4080. });
  4081. },
  4082. alert: function() {
  4083. var config = this.callParent(arguments);
  4084. if (config.buttonName) {
  4085. config.buttons = [
  4086. config.buttonName
  4087. ];
  4088. }
  4089. this.show(config);
  4090. },
  4091. confirm: function() {
  4092. var config = this.callParent(arguments);
  4093. this.show(config);
  4094. },
  4095. prompt: function() {
  4096. var config = this.callParent(arguments),
  4097. buttons = [],
  4098. ln = config.buttons.length,
  4099. button, i, callback;
  4100. //buttons
  4101. for (i = 0; i < ln; i++) {
  4102. button = config.buttons[i];
  4103. if (Ext.isString(button)) {
  4104. button = {
  4105. text: config.buttons[i],
  4106. itemId: config.buttons[i].toLowerCase()
  4107. };
  4108. }
  4109. buttons.push(button);
  4110. }
  4111. this.msg = Ext.create('Ext.MessageBox');
  4112. callback = function(buttonText, value) {
  4113. if (config.callback) {
  4114. config.callback.apply(config.scope, [
  4115. buttonText,
  4116. value
  4117. ]);
  4118. }
  4119. };
  4120. this.msg.prompt(config.title, config.message, callback, this.msg, config.multiLine, config.value, config.prompt);
  4121. },
  4122. beep: function(times) {
  4123. if (!Ext.isNumber(times)) {
  4124. times = 1;
  4125. }
  4126. var count = 0;
  4127. var callback = function() {
  4128. if (count < times) {
  4129. Ext.defer(function() {
  4130. Ext.util.Audio.beep(callback);
  4131. }, 50);
  4132. }
  4133. count++;
  4134. };
  4135. callback();
  4136. },
  4137. vibrate: function() {
  4138. //nice animation to fake vibration
  4139. var animation = [
  4140. "@-webkit-keyframes vibrate{",
  4141. " from {",
  4142. " -webkit-transform: rotate(-2deg);",
  4143. " }",
  4144. " to{",
  4145. " -webkit-transform: rotate(2deg);",
  4146. " }",
  4147. "}",
  4148. "body {",
  4149. " -webkit-animation: vibrate 50ms linear 10 alternate;",
  4150. "}"
  4151. ];
  4152. var head = document.getElementsByTagName("head")[0];
  4153. var cssNode = document.createElement('style');
  4154. cssNode.innerHTML = animation.join('\n');
  4155. head.appendChild(cssNode);
  4156. Ext.defer(function() {
  4157. head.removeChild(cssNode);
  4158. }, 400);
  4159. }
  4160. });
  4161. /**
  4162. * Provides a cross device way to show notifications. There are three different implementations:
  4163. *
  4164. * - Sencha Packager
  4165. * - Cordova
  4166. * - Simulator
  4167. *
  4168. * When this singleton is instantiated, it will automatically use the correct implementation depending on the current device.
  4169. *
  4170. * Both the Sencha Packager and Cordova versions will use the native implementations to display the notification. The
  4171. * Simulator implementation will use {@link Ext.MessageBox} for {@link #show} and a simply animation when you call {@link #vibrate}.
  4172. *
  4173. * ## Examples
  4174. *
  4175. * To show a simple notification:
  4176. *
  4177. * Ext.device.Notification.show({
  4178. * title: 'Verification',
  4179. * message: 'Is your email address: test@sencha.com',
  4180. * buttons: Ext.MessageBox.OKCANCEL,
  4181. * callback: function(button) {
  4182. * if (button === "ok") {
  4183. * console.log('Verified');
  4184. * } else {
  4185. * console.log('Nope');
  4186. * }
  4187. * }
  4188. * });
  4189. *
  4190. * To make the device vibrate:
  4191. *
  4192. * Ext.device.Notification.vibrate();
  4193. *
  4194. * @mixins Ext.device.notification.Abstract
  4195. */
  4196. Ext.define('Ext.device.Notification', {
  4197. singleton: true,
  4198. requires: [
  4199. 'Ext.device.Communicator',
  4200. 'Ext.device.notification.Cordova',
  4201. 'Ext.device.notification.Simulator'
  4202. ],
  4203. constructor: function() {
  4204. var browserEnv = Ext.browser.is;
  4205. if (browserEnv.WebView) {
  4206. if (browserEnv.Cordova) {
  4207. return Ext.create('Ext.device.notification.Cordova');
  4208. }
  4209. }
  4210. return Ext.create('Ext.device.notification.Simulator');
  4211. }
  4212. });
  4213. /**
  4214. * @private
  4215. */
  4216. Ext.define('Ext.device.orientation.Abstract', {
  4217. mixins: [
  4218. 'Ext.mixin.Observable'
  4219. ],
  4220. /**
  4221. * @event orientationchange
  4222. * Fires when the orientation has been changed on this device.
  4223. *
  4224. * Ext.device.Orientation.on({
  4225. * scope: this,
  4226. * orientationchange: function(e) {
  4227. * console.log('Alpha: ', e.alpha);
  4228. * console.log('Beta: ', e.beta);
  4229. * console.log('Gamma: ', e.gamma);
  4230. * }
  4231. * });
  4232. *
  4233. * @param {Object} event The event object
  4234. * @param {Object} event.alpha The alpha value of the orientation event
  4235. * @param {Object} event.beta The beta value of the orientation event
  4236. * @param {Object} event.gamma The gamma value of the orientation event
  4237. */
  4238. onDeviceOrientation: function(e) {
  4239. this.doFireEvent('orientationchange', [
  4240. e
  4241. ]);
  4242. }
  4243. });
  4244. /**
  4245. * Provides the HTML5 implementation for the orientation API.
  4246. * @private
  4247. */
  4248. Ext.define('Ext.device.orientation.HTML5', {
  4249. extend: 'Ext.device.orientation.Abstract',
  4250. constructor: function() {
  4251. this.callParent(arguments);
  4252. this.onDeviceOrientation = Ext.Function.bind(this.onDeviceOrientation, this);
  4253. window.addEventListener('deviceorientation', this.onDeviceOrientation, true);
  4254. }
  4255. });
  4256. /**
  4257. * This class provides you with a cross platform way of listening to when the the orientation changes on the
  4258. * device your application is running on.
  4259. *
  4260. * The {@link Ext.device.Orientation#orientationchange orientationchange} event gets passes the `alpha`, `beta` and
  4261. * `gamma` values. ** These properties only exist when packaging with the Sencha Native Packager. **
  4262. *
  4263. * You can find more information about these values and how to use them on the [W3C device orientation specification](http://dev.w3.org/geo/api/spec-source-orientation.html#deviceorientation).
  4264. *
  4265. * ## Example
  4266. *
  4267. * To listen to the device orientation, you can do the following:
  4268. *
  4269. * Ext.device.Orientation.on({
  4270. * scope: this,
  4271. * orientationchange: function(e) {
  4272. * console.log('Alpha: ', e.alpha);
  4273. * console.log('Beta: ', e.beta);
  4274. * console.log('Gamma: ', e.gamma);
  4275. * }
  4276. * });
  4277. *
  4278. * @mixins Ext.device.orientation.Abstract
  4279. */
  4280. Ext.define('Ext.device.Orientation', {
  4281. singleton: true,
  4282. requires: [
  4283. 'Ext.device.Communicator',
  4284. 'Ext.device.orientation.HTML5'
  4285. ],
  4286. constructor: function() {
  4287. return Ext.create('Ext.device.orientation.HTML5');
  4288. }
  4289. });
  4290. /**
  4291. * @private
  4292. */
  4293. Ext.define('Ext.device.push.Abstract', {
  4294. /**
  4295. * @property
  4296. * Notification type: alert.
  4297. */
  4298. ALERT: 1,
  4299. /**
  4300. * @property
  4301. * Notification type: badge.
  4302. */
  4303. BADGE: 2,
  4304. /**
  4305. * @property
  4306. * Notification type: sound.
  4307. */
  4308. SOUND: 4,
  4309. /**
  4310. * @method getInitialConfig
  4311. * @hide
  4312. */
  4313. /**
  4314. * Registers a push notification.
  4315. *
  4316. * Ext.device.Push.register({
  4317. * type: Ext.device.Push.ALERT|Ext.device.Push.BADGE|Ext.device.Push.SOUND,
  4318. * success: function(token) {
  4319. * console.log('# Push notification registration successful:');
  4320. * console.log(' token: ' + token);
  4321. * },
  4322. * failure: function(error) {
  4323. * console.log('# Push notification registration unsuccessful:');
  4324. * console.log(' error: ' + error);
  4325. * },
  4326. * received: function(notifications) {
  4327. * console.log('# Push notification received:');
  4328. * console.log(' ' + JSON.stringify(notifications));
  4329. * }
  4330. * });
  4331. *
  4332. * @param {Object} config
  4333. * The configuration for to pass when registering this push notification service.
  4334. *
  4335. * @param {Number} config.type
  4336. * The type(s) of notifications to enable. Available options are:
  4337. *
  4338. * - {@link Ext.device.Push#ALERT}
  4339. * - {@link Ext.device.Push#BADGE}
  4340. * - {@link Ext.device.Push#SOUND}
  4341. *
  4342. * **Usage**
  4343. *
  4344. * Enable alerts and badges:
  4345. *
  4346. * Ext.device.Push.register({
  4347. * type: Ext.device.Push.ALERT|Ext.device.Push.BADGE
  4348. * // ...
  4349. * });
  4350. *
  4351. * Enable alerts, badges and sounds:
  4352. *
  4353. * Ext.device.Push.register({
  4354. * type: Ext.device.Push.ALERT|Ext.device.Push.BADGE|Ext.device.Push.SOUND
  4355. * // ...
  4356. * });
  4357. *
  4358. * Enable only sounds:
  4359. *
  4360. * Ext.device.Push.register({
  4361. * type: Ext.device.Push.SOUND
  4362. * // ...
  4363. * });
  4364. *
  4365. * @param {Function} config.success
  4366. * The callback to be called when registration is complete.
  4367. *
  4368. * @param {String} config.success.token
  4369. * A unique token for this push notification service.
  4370. *
  4371. * @param {Function} config.failure
  4372. * The callback to be called when registration fails.
  4373. *
  4374. * @param {String} config.failure.error
  4375. * The error message.
  4376. *
  4377. * @param {Function} config.received
  4378. * The callback to be called when a push notification is received on this device.
  4379. *
  4380. * @param {Object} config.received.notifications
  4381. * The notifications that have been received.
  4382. */
  4383. register: function(config) {
  4384. var me = this;
  4385. if (!config.received) {
  4386. Ext.Logger.error('Failed to pass a received callback. This is required.');
  4387. }
  4388. if (config.type == null) {
  4389. Ext.Logger.error('Failed to pass a type. This is required.');
  4390. }
  4391. return {
  4392. success: function(token) {
  4393. me.onSuccess(token, config.success, config.scope || me);
  4394. },
  4395. failure: function(error) {
  4396. me.onFailure(error, config.failure, config.scope || me);
  4397. },
  4398. received: function(notifications) {
  4399. me.onReceived(notifications, config.received, config.scope || me);
  4400. },
  4401. type: config.type
  4402. };
  4403. },
  4404. onSuccess: function(token, callback, scope) {
  4405. if (callback) {
  4406. callback.call(scope, token);
  4407. }
  4408. },
  4409. onFailure: function(error, callback, scope) {
  4410. if (callback) {
  4411. callback.call(scope, error);
  4412. }
  4413. },
  4414. onReceived: function(notifications, callback, scope) {
  4415. if (callback) {
  4416. callback.call(scope, notifications);
  4417. }
  4418. }
  4419. });
  4420. /**
  4421. * @private
  4422. * Interfaces with Cordova PushPlugin: https://github.com/phonegap-build/PushPlugin
  4423. */
  4424. Ext.define('Ext.device.push.Cordova', {
  4425. extend: 'Ext.device.push.Abstract',
  4426. statics: {
  4427. /**
  4428. * @private
  4429. * A collection of callback methods that can be globally called by the Cordova PushPlugin
  4430. */
  4431. callbacks: {}
  4432. },
  4433. setPushConfig: function(config) {
  4434. var methodName = Ext.id(null, 'callback');
  4435. //Cordova's PushPlugin needs a static method to call when notifications are received
  4436. Ext.device.push.Cordova.callbacks[methodName] = config.callbacks.received;
  4437. return {
  4438. "badge": (config.callbacks.type === Ext.device.Push.BADGE) ? "true" : "false",
  4439. "sound": (config.callbacks.type === Ext.device.Push.SOUND) ? "true" : "false",
  4440. "alert": (config.callbacks.type === Ext.device.Push.ALERT) ? "true" : "false",
  4441. "ecb": 'Ext.device.push.Cordova.callbacks.' + methodName,
  4442. "senderID": config.senderID
  4443. };
  4444. },
  4445. register: function() {
  4446. var config = arguments[0];
  4447. config.callbacks = this.callParent(arguments);
  4448. var pushConfig = this.setPushConfig(config),
  4449. plugin = window.plugins.pushNotification;
  4450. plugin.register(config.callbacks.success, config.callbacks.failure, pushConfig);
  4451. }
  4452. });
  4453. /**
  4454. * Provides a way to send push notifications to a device.
  4455. *
  4456. * # Example
  4457. *
  4458. * Ext.device.Push.register({
  4459. * type: Ext.device.Push.ALERT|Ext.device.Push.BADGE|Ext.device.Push.SOUND,
  4460. * success: function(token) {
  4461. * console.log('# Push notification registration successful:');
  4462. * console.log(' token: ' + token);
  4463. * },
  4464. * failure: function(error) {
  4465. * console.log('# Push notification registration unsuccessful:');
  4466. * console.log(' error: ' + error);
  4467. * },
  4468. * received: function(notifications) {
  4469. * console.log('# Push notification received:');
  4470. * console.log(' ' + JSON.stringify(notifications));
  4471. * }
  4472. * });
  4473. *
  4474. *
  4475. * ## Sencha Cmd
  4476. *
  4477. * Currently only available on iOS for apps packaged with Sencha Cmd.
  4478. *
  4479. * ## Cordova / PhoneGap
  4480. *
  4481. * For apps packaged with Cordova or PhoneGap, Ext.device.Push currently supports iOS and
  4482. * Android via the [PushPlugin](https://github.com/phonegap-build/PushPlugin).
  4483. *
  4484. * Be sure to include that plugin in your project; Ext.device.Push simply normalizes the
  4485. * interface for using notifications in your application.
  4486. *
  4487. * @mixins Ext.device.push.Abstract
  4488. */
  4489. Ext.define('Ext.device.Push', {
  4490. singleton: true,
  4491. requires: [
  4492. 'Ext.device.Communicator',
  4493. 'Ext.device.push.Cordova'
  4494. ],
  4495. constructor: function() {
  4496. var browserEnv = Ext.browser.is;
  4497. if (browserEnv.WebView) {
  4498. if (browserEnv.Cordova) {
  4499. return Ext.create('Ext.device.push.Cordova');
  4500. }
  4501. }
  4502. return Ext.create('Ext.device.push.Abstract');
  4503. }
  4504. });
  4505. /**
  4506. * @private
  4507. */
  4508. Ext.define('Ext.device.splashscreen.Abstract', {
  4509. show: Ext.emptyFn,
  4510. hide: Ext.emptyFn
  4511. });
  4512. /**
  4513. * @private
  4514. */
  4515. Ext.define('Ext.device.splashscreen.Cordova', {
  4516. alternateClassName: 'Ext.device.splashscreen.PhoneGap',
  4517. extend: 'Ext.device.splashscreen.Abstract',
  4518. show: function() {
  4519. navigator.splashscreen.show();
  4520. },
  4521. hide: function() {
  4522. navigator.splashscreen.hide();
  4523. }
  4524. });
  4525. /**
  4526. * @private
  4527. */
  4528. Ext.define('Ext.device.splashscreen.Simulator', {
  4529. extend: 'Ext.device.splashscreen.Abstract'
  4530. });
  4531. /**
  4532. * Provides access to the native Splashscreen API
  4533. *
  4534. * - [PhoneGap](http://docs.phonegap.com/en/2.6.0/cordova_splashscreen_splashscreen.md.html#Splashscreen)
  4535. *
  4536. * Class currently only works with Cordova and does not have a simulated HTML counter part.
  4537. * Please see notes on Cordova Docs for proper Native project code changes that
  4538. * will need to be made to use this plugin.
  4539. *
  4540. * http://docs.phonegap.com/en/2.6.0/cordova_splashscreen_splashscreen.md.html#Splashscreen
  4541. */
  4542. Ext.define('Ext.device.Splashscreen', {
  4543. singleton: true,
  4544. requires: [
  4545. 'Ext.device.splashscreen.Cordova',
  4546. 'Ext.device.splashscreen.Simulator'
  4547. ],
  4548. constructor: function() {
  4549. var browserEnv = Ext.browser.is;
  4550. if (browserEnv.WebView) {
  4551. if (browserEnv.Cordova) {
  4552. return Ext.create('Ext.device.splashscreen.Cordova');
  4553. }
  4554. }
  4555. return Ext.create('Ext.device.splashscreen.Simulator');
  4556. }
  4557. });
  4558. /**
  4559. * @private
  4560. */
  4561. Ext.define('Ext.device.storage.Abstract', {
  4562. config: {
  4563. databaseName: "Sencha",
  4564. databaseVersion: '1.0',
  4565. databaseDisplayName: 'Sencha Database',
  4566. databaseSize: 5 * 1024 * 1024
  4567. },
  4568. openDatabase: function(config) {
  4569. var defaultConfig = Ext.device.storage.Abstract.prototype.config;
  4570. config = Ext.applyIf(config, {
  4571. name: defaultConfig.databaseName,
  4572. version: defaultConfig.databaseVersion,
  4573. displayName: defaultConfig.databaseDisplayName,
  4574. size: defaultConfig.databaseSize
  4575. });
  4576. return config;
  4577. },
  4578. numKeys: Ext.emptyFn,
  4579. getKey: Ext.emptyFn,
  4580. getItem: Ext.emptyFn,
  4581. setItem: Ext.emptyFn,
  4582. removeItem: Ext.emptyFn,
  4583. clear: Ext.emptyFn
  4584. });
  4585. /**
  4586. * @private
  4587. */
  4588. Ext.define("Ext.device.storage.HTML5.SQLStatement", {
  4589. extend: 'Ext.Base',
  4590. sql: null,
  4591. "arguments": null,
  4592. success: Ext.emptyFn,
  4593. failure: Ext.emptyFn,
  4594. constructor: function(config) {
  4595. this.sql = config.sql;
  4596. this.arguments = config.arguments;
  4597. this.success = config.success;
  4598. this.failure = config.failure;
  4599. }
  4600. });
  4601. /**
  4602. * @private
  4603. */
  4604. Ext.define('Ext.device.storage.HTML5.Database', {
  4605. requires: [
  4606. "Ext.device.storage.HTML5.SQLStatement"
  4607. ],
  4608. db: null,
  4609. constructor: function(config) {
  4610. this.db = window.openDatabase(config.name, config.version, config.displayName, config.size);
  4611. },
  4612. getVersion: function() {
  4613. if (this.db) {
  4614. return this.db.version;
  4615. }
  4616. // <debug>
  4617. Ext.Logger.warn('Database has not been opened before calling function #getVersion');
  4618. // </debug>
  4619. return null;
  4620. },
  4621. /**
  4622. * @param {String/String[]/Object/Object[]/SQLStatement/SQLStatement[]} sql SQL Command to run with optional arguments and callbacks
  4623. * @param {Function} success callback for successful transaction
  4624. * @param {Function} failure callback for failed transaction
  4625. */
  4626. transaction: function(sql, success, failure) {
  4627. if (!this.db) {
  4628. // <debug>
  4629. Ext.Logger.warn('Database has not been opened before calling function #transaction');
  4630. // </debug>
  4631. return;
  4632. }
  4633. if (!Ext.isArray(sql)) {
  4634. sql = [
  4635. sql
  4636. ];
  4637. }
  4638. var txFn = function(tx) {
  4639. Ext.each(sql, function(sqlStatement) {
  4640. if (Ext.isString(sqlStatement)) {
  4641. tx.executeSql(sqlStatement);
  4642. } else if (Ext.isObject(sqlStatement)) {
  4643. tx.executeSql(sqlStatement.sql, sqlStatement.arguments, sqlStatement.success, sqlStatement.failure);
  4644. }
  4645. });
  4646. };
  4647. this.db.transaction(txFn, failure, success);
  4648. }
  4649. });
  4650. /**
  4651. * @private
  4652. */
  4653. Ext.define('Ext.device.storage.HTML5.HTML5', {
  4654. extend: 'Ext.device.storage.Abstract',
  4655. requires: [
  4656. 'Ext.device.storage.HTML5.Database'
  4657. ],
  4658. dbCache: {},
  4659. openDatabase: function(config) {
  4660. config = this.callParent(arguments);
  4661. if (!this.dbCache[config.name] || config.noCache) {
  4662. this.dbCache[config.name] = Ext.create('Ext.device.storage.HTML5.Database', config);
  4663. }
  4664. return this.dbCache[config.name];
  4665. },
  4666. numKeys: function() {
  4667. return window.localStorage.length;
  4668. },
  4669. getKey: function(index) {
  4670. return window.localStorage.key(index);
  4671. },
  4672. getItem: function(key) {
  4673. return window.localStorage.getItem(key);
  4674. },
  4675. setItem: function(key, value) {
  4676. return window.localStorage.setItem(key, value);
  4677. },
  4678. removeItem: function(key) {
  4679. return window.localStorage.removeItem(key);
  4680. },
  4681. clear: function() {
  4682. return window.localStorage.clear();
  4683. }
  4684. });
  4685. /**
  4686. * @private
  4687. */
  4688. Ext.define('Ext.device.storage.Cordova', {
  4689. alternateClassName: 'Ext.device.storage.PhoneGap',
  4690. extend: 'Ext.device.storage.HTML5.HTML5'
  4691. });
  4692. /**
  4693. * @private
  4694. */
  4695. Ext.define('Ext.device.storage.Simulator', {
  4696. extend: 'Ext.device.storage.HTML5.HTML5'
  4697. });
  4698. /**
  4699. *
  4700. */
  4701. Ext.define('Ext.device.Storage', {
  4702. singleton: true,
  4703. requires: [
  4704. 'Ext.device.storage.Cordova',
  4705. 'Ext.device.storage.Simulator'
  4706. ],
  4707. constructor: function() {
  4708. var browserEnv = Ext.browser.is;
  4709. if (browserEnv.WebView) {
  4710. if (browserEnv.Cordova) {
  4711. return Ext.create('Ext.device.storage.Cordova');
  4712. }
  4713. }
  4714. return Ext.create('Ext.device.storage.Simulator');
  4715. }
  4716. });
  4717. /**
  4718. * @private
  4719. */
  4720. Ext.define('Ext.device.twitter.Abstract', {
  4721. /**
  4722. * Pops up a Twitter compose sheet view with your specified tweet.
  4723. *
  4724. * @param {Object} config An object which contains the following config options:
  4725. *
  4726. * @param {String} config.tweet The default tweet text to add to the compose window.
  4727. *
  4728. * @param {String} config.url An optional URL to attatch to the Tweet.
  4729. *
  4730. * @param {String} config.image An optional image URL to attatch to the Tweet.
  4731. *
  4732. * @param {Function} config.success The callback when the Tweet is successfully posted.
  4733. *
  4734. * @param {Function} config.failure The callback when the Tweet is unsuccessfully posted.
  4735. */
  4736. compose: Ext.emptyFn,
  4737. /**
  4738. * Gets Tweets from Twitter Timeline
  4739. *
  4740. * @param {Object} config An object which contains the following config options:
  4741. *
  4742. * @param {Function} config.success callback
  4743. * @param {Object[]} config.success.response Tweet objects, see [Twitter Timeline Doc]
  4744. *
  4745. * @param {Function} config.failure callback
  4746. * @param {String} config.failure.error reason for failure
  4747. *
  4748. * [Twitter Timeline Doc]: https://dev.twitter.com/docs/api/1/get/statuses/public_timeline
  4749. */
  4750. getPublicTimeline: Ext.emptyFn,
  4751. /**
  4752. * Gets Tweets from Twitter Mentions
  4753. *
  4754. * @param {Object} config An object which contains the following config options:
  4755. *
  4756. * @param {Function} config.success callback
  4757. * @param {Object[]} config.success.response Tweet objects, see [Twitter Mentions Doc]
  4758. *
  4759. * @param {Function} config.failure callback
  4760. * @param {String} config.failure.error reason for failure
  4761. *
  4762. * [Twitter Timeline Doc]: https://dev.twitter.com/docs/api/1/get/statuses/public_timeline
  4763. */
  4764. getMentions: Ext.emptyFn,
  4765. /**
  4766. * Gets a specific Twitter user info
  4767. *
  4768. * @param {Object} config An object which contains the following config options:
  4769. *
  4770. * @param {Function} config.success callback
  4771. * @param {Object[]} config.success.response The JSON response form twitter
  4772. *
  4773. * @param {Function} config.failure callback
  4774. * @param {String} config.failure.error reason for failure
  4775. */
  4776. getTwitterUsername: Ext.emptyFn,
  4777. /**
  4778. * Gets a specific Twitter user info
  4779. *
  4780. * @param {Object} config An object which contains the following config options:
  4781. *
  4782. * @param {String} config.url of [Twitter API Endpoint]
  4783. *
  4784. * @param {Object} config.params key-value map, matching [Twitter API Endpoint]
  4785. *
  4786. * @param {Object} config.options (optional) other options for the HTTP request
  4787. * @param {String} config.options.requestMethod HTTP Request type, ex: "POST"
  4788. *
  4789. * @param {Function} config.success callback
  4790. * @param {Object[]} config.success.response objects returned from Twitter API (Tweets, Users,...)
  4791. *
  4792. * @param {Function} config.failure callback
  4793. * @param {String} config.failure.error reason for failure
  4794. *
  4795. * [Twitter API Endpoint]: https://dev.twitter.com/docs/api
  4796. */
  4797. getTwitterRequest: Ext.emptyFn
  4798. });
  4799. /**
  4800. * @private
  4801. */
  4802. Ext.define('Ext.device.twitter.Cordova', {
  4803. compose: function(config) {
  4804. window.plugins.twitter.composeTweet(config.success, config.failure, config.tweet, {
  4805. urlAttach: config.url,
  4806. imageAttach: config.image
  4807. });
  4808. },
  4809. getPublicTimeline: function(config) {
  4810. window.plugins.twitter.getPublicTimeline(config.success, config.failure);
  4811. },
  4812. getMentions: function(config) {
  4813. window.plugins.twitter.getMentions(config.success, config.failure);
  4814. },
  4815. getTwitterUsername: function(config) {
  4816. window.plugins.twitter.getTwitterUsername(config.success, config.failure);
  4817. },
  4818. getTwitterRequest: function(config) {
  4819. window.plugins.twitter.getTWRequest(config.url, config.params, config.success, config.failure, config.options);
  4820. }
  4821. });
  4822. /**
  4823. * Allows you to interact with the Twitter API on iOS devices from within your Cordova application.
  4824. *
  4825. * For setup information, please read the [plugin guide](https://github.com/phonegap/phonegap-plugins/tree/master/iOS/Twitter).
  4826. *
  4827. * @mixins Ext.device.twitter.Abstract
  4828. */
  4829. Ext.define('Ext.device.Twitter', {
  4830. alternateClassName: 'Ext.ux.device.Twitter',
  4831. singleton: true,
  4832. requires: [
  4833. 'Ext.device.Communicator',
  4834. 'Ext.device.twitter.*'
  4835. ],
  4836. constructor: function() {
  4837. var browserEnv = Ext.browser.is;
  4838. if (browserEnv.WebView && browserEnv.Cordova) {
  4839. return Ext.create('Ext.device.twitter.Cordova');
  4840. } else {
  4841. return Ext.create('Ext.device.twitter.Abstract');
  4842. }
  4843. }
  4844. });
  4845. /**
  4846. * @private
  4847. */
  4848. Ext.define('Ext.device.browser.Window', {
  4849. extend: 'Ext.Evented',
  4850. open: function(config) {
  4851. var me = this;
  4852. this._window = window.open(config.url, config.showToolbar ? '_blank' : '_self', config.options || null);
  4853. // Add events
  4854. this._window.addEventListener('loadstart', function() {
  4855. me.fireEvent('loadstart', me);
  4856. });
  4857. this._window.addEventListener('loadstop', function() {
  4858. me.fireEvent('loadstop', me);
  4859. });
  4860. this._window.addEventListener('loaderror', function() {
  4861. me.fireEvent('loaderror', me);
  4862. });
  4863. this._window.addEventListener('exit', function() {
  4864. me.fireEvent('close', me);
  4865. });
  4866. },
  4867. close: function() {
  4868. if (!this._window) {
  4869. return;
  4870. }
  4871. this._window.close();
  4872. }
  4873. });