xscroll.js 168 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320
  1. ;(function() {
  2. var util = {}, events = {}, base = {}, easing = {}, timer = {}, animate = {}, hammer = {}, boundry = {}, components_sticky = {}, components_fixed = {}, core = {}, components_scrollbar = {}, components_controller = {}, simulate_scroll = {}, origin_scroll = {}, xscroll = {};
  3. util = function (exports) {
  4. var SUBSTITUTE_REG = /\\?\{([^{}]+)\}/g, EMPTY = '';
  5. var RE_TRIM = /^[\s\xa0]+|[\s\xa0]+$/g, trim = String.prototype.trim;
  6. var _trim = trim ? function (str) {
  7. return str == null ? EMPTY : trim.call(str);
  8. } : function (str) {
  9. return str == null ? EMPTY : (str + '').replace(RE_TRIM, EMPTY);
  10. };
  11. function upperCase() {
  12. return arguments[1].toUpperCase();
  13. }
  14. function Empty() {
  15. }
  16. function createObject(proto, constructor) {
  17. var newProto;
  18. if (Object.create) {
  19. newProto = Object.create(proto);
  20. } else {
  21. Empty.prototype = proto;
  22. newProto = new Empty();
  23. }
  24. newProto.constructor = constructor;
  25. return newProto;
  26. }
  27. function getNodes(node, rootNode) {
  28. if (!node)
  29. return;
  30. if (node.nodeType)
  31. return [node];
  32. var rootNode = rootNode && rootNode.nodeType ? rootNode : document;
  33. if (node && typeof node === 'string') {
  34. return rootNode.querySelectorAll(node);
  35. }
  36. return;
  37. }
  38. // Useful for temporary DOM ids.
  39. var idCounter = 0;
  40. var getOffsetTop = function (el) {
  41. var offset = el.offsetTop;
  42. if (el.offsetParent != null)
  43. offset += getOffsetTop(el.offsetParent);
  44. return offset;
  45. };
  46. var getOffsetLeft = function (el) {
  47. var offset = el.offsetLeft;
  48. if (el.offsetParent != null)
  49. offset += getOffsetLeft(el.offsetParent);
  50. return offset;
  51. };
  52. var Util = {
  53. // Is a given variable an object?
  54. isObject: function (obj) {
  55. return obj === Object(obj);
  56. },
  57. isArray: Array.isArray || function (obj) {
  58. return toString.call(obj) == '[object Array]';
  59. },
  60. // Is a given array, string, or object empty?
  61. // An "empty" object has no enumerable own-properties.
  62. isEmpty: function (obj) {
  63. if (obj == null)
  64. return true;
  65. if (this.isArray(obj) || this.isString(obj))
  66. return obj.length === 0;
  67. for (var key in obj)
  68. if (this.has(obj, key))
  69. return false;
  70. return true;
  71. },
  72. mix: function (to, from, deep) {
  73. for (var i in from) {
  74. to[i] = from[i];
  75. }
  76. return to;
  77. },
  78. extend: function (r, s, px, sx) {
  79. if (!s || !r) {
  80. return r;
  81. }
  82. var sp = s.prototype, rp;
  83. // add prototype chain
  84. rp = createObject(sp, r);
  85. r.prototype = this.mix(rp, r.prototype);
  86. r.superclass = createObject(sp, s);
  87. // add prototype overrides
  88. if (px) {
  89. this.mix(rp, px);
  90. }
  91. // add object overrides
  92. if (sx) {
  93. this.mix(r, sx);
  94. }
  95. return r;
  96. },
  97. /**
  98. * test whether a string start with a specified substring
  99. * @param {String} str the whole string
  100. * @param {String} prefix a specified substring
  101. * @return {Boolean} whether str start with prefix
  102. * @member util
  103. */
  104. startsWith: function (str, prefix) {
  105. return str.lastIndexOf(prefix, 0) === 0;
  106. },
  107. /**
  108. * test whether a string end with a specified substring
  109. * @param {String} str the whole string
  110. * @param {String} suffix a specified substring
  111. * @return {Boolean} whether str end with suffix
  112. * @member util
  113. */
  114. endsWith: function (str, suffix) {
  115. var ind = str.length - suffix.length;
  116. return ind >= 0 && str.indexOf(suffix, ind) === ind;
  117. },
  118. /**
  119. * Removes the whitespace from the beginning and end of a string.
  120. * @method
  121. * @member util
  122. */
  123. trim: _trim,
  124. /**
  125. * Substitutes keywords in a string using an object/array.
  126. * Removes undef keywords and ignores escaped keywords.
  127. * @param {String} str template string
  128. * @param {Object} o json data
  129. * @member util
  130. * @param {RegExp} [regexp] to match a piece of template string
  131. */
  132. substitute: function (str, o, regexp) {
  133. if (typeof str !== 'string' || !o) {
  134. return str;
  135. }
  136. return str.replace(regexp || SUBSTITUTE_REG, function (match, name) {
  137. if (match.charAt(0) === '\\') {
  138. return match.slice(1);
  139. }
  140. return o[name] === undefined ? EMPTY : o[name];
  141. });
  142. },
  143. /**
  144. * vendors
  145. * @return { String } webkit|moz|ms|o
  146. * @memberOf Util
  147. */
  148. vendor: function () {
  149. var el = document.createElement('div').style;
  150. var vendors = [
  151. 't',
  152. 'webkitT',
  153. 'MozT',
  154. 'msT',
  155. 'OT'
  156. ], transform, i = 0, l = vendors.length;
  157. for (; i < l; i++) {
  158. transform = vendors[i] + 'ransform';
  159. if (transform in el)
  160. return vendors[i].substr(0, vendors[i].length - 1);
  161. }
  162. return false;
  163. }(),
  164. /**
  165. * add vendor to attribute
  166. * @memberOf Util
  167. * @param {String} attrName name of attribute
  168. * @return { String }
  169. **/
  170. prefixStyle: function (attrName) {
  171. if (this.vendor === false)
  172. return false;
  173. if (this.vendor === '')
  174. return attrName;
  175. return this.vendor + attrName.charAt(0).toUpperCase() + attrName.substr(1);
  176. },
  177. /**
  178. * judge if has class
  179. * @memberOf Util
  180. * @param {HTMLElement} el
  181. * @param {String} className
  182. * @return {Boolean}
  183. */
  184. hasClass: function (el, className) {
  185. return el && el.className && className && el.className.indexOf(className) != -1;
  186. },
  187. /**
  188. * add className for the element
  189. * @memberOf Util
  190. * @param {HTMLElement} el
  191. * @param {String} className
  192. */
  193. addClass: function (el, className) {
  194. if (el && className && !this.hasClass(el, className)) {
  195. el.className += ' ' + className;
  196. }
  197. },
  198. /**
  199. * remove className for the element
  200. * @memberOf Util
  201. * @param {HTMLElement} el
  202. * @param {String} className
  203. */
  204. removeClass: function (el, className) {
  205. if (el && el.className && className) {
  206. el.className = el.className.replace(className, '');
  207. }
  208. },
  209. /**
  210. * remove an element
  211. * @memberOf Util
  212. * @param {HTMLElement} el
  213. */
  214. remove: function (el) {
  215. if (!el || !el.parentNode)
  216. return;
  217. el.parentNode.removeChild(el);
  218. },
  219. /**
  220. * get offset top
  221. * @memberOf Util
  222. * @param {HTMLElement} el
  223. * @return {Number} offsetTop
  224. */
  225. getOffsetTop: getOffsetTop,
  226. /**
  227. * get offset left
  228. * @memberOf Util
  229. * @param {HTMLElement} el
  230. * @return {Number} offsetLeft
  231. */
  232. getOffsetLeft: getOffsetLeft,
  233. /**
  234. * get offset left
  235. * @memberOf Util
  236. * @param {HTMLElement} el
  237. * @param {String} selector
  238. * @param {HTMLElement} rootNode
  239. * @return {HTMLElement} parent element
  240. */
  241. findParentEl: function (el, selector, rootNode) {
  242. var rs = null, parent = null;
  243. var type = /^#/.test(selector) ? 'id' : /^\./.test(selector) ? 'class' : 'tag';
  244. var sel = selector.replace(/\.|#/g, '');
  245. if (rootNode && typeof rootNode === 'string') {
  246. rootNode = document.querySelector(rootNode);
  247. }
  248. rootNode = rootNode || document.body;
  249. if (!el || !selector)
  250. return;
  251. if (type == 'class' && el.className && el.className.match(sel)) {
  252. return el;
  253. } else if (type == 'id' && el.id && _trim(el.id) == sel) {
  254. return el;
  255. } else if (type == 'tag' && el.tagName.toLowerCase() == sel) {
  256. return el;
  257. }
  258. while (!rs) {
  259. if (parent == rootNode)
  260. break;
  261. parent = el.parentNode;
  262. if (!parent)
  263. break;
  264. if (type == 'class' && parent.className && parent.className.match(sel) || type == 'id' && parent.id && _trim(parent.id) == sel || type == 'tag' && parent.tagName && parent.tagName.toLowerCase() == sel) {
  265. rs = parent;
  266. return rs;
  267. break;
  268. } else {
  269. el = parent;
  270. }
  271. }
  272. return null;
  273. },
  274. /**
  275. * Generate a unique integer id (unique within the entire client session).
  276. * @param {String} prefix
  277. * @return {String} guid
  278. */
  279. guid: function (prefix) {
  280. var id = ++idCounter + '';
  281. return prefix ? prefix + id : id;
  282. },
  283. /**
  284. * judge if is an android os
  285. * @return {Boolean} [description]
  286. */
  287. isAndroid: function () {
  288. return /Android /.test(window.navigator.appVersion);
  289. },
  290. /**
  291. * judge if is an android device with low performance
  292. * @return {Boolean}
  293. */
  294. isBadAndroid: function () {
  295. return /Android /.test(window.navigator.appVersion) && !/Chrome\/\d/.test(window.navigator.appVersion);
  296. },
  297. px2Num: function (px) {
  298. return Number(px.replace(/px/, ''));
  299. },
  300. getNodes: getNodes,
  301. getNode: function (node, rootNode) {
  302. var nodes = getNodes(node, rootNode);
  303. return nodes && nodes[0];
  304. },
  305. stringifyStyle: function (style) {
  306. var styleStr = '';
  307. for (var i in style) {
  308. styleStr += [
  309. i,
  310. ':',
  311. style[i],
  312. ';'
  313. ].join('');
  314. }
  315. return styleStr;
  316. }
  317. };
  318. // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
  319. var names = [
  320. 'Arguments',
  321. 'Function',
  322. 'String',
  323. 'Number',
  324. 'Date',
  325. 'RegExp'
  326. ];
  327. for (var i = 0; i < names.length; i++) {
  328. Util['is' + names[i]] = function (obj) {
  329. return toString.call(obj) == '[object ' + names[i] + ']';
  330. };
  331. }
  332. if (typeof module == 'object' && module.exports) {
  333. exports = Util;
  334. } /** ignored by jsdoc **/ else {
  335. return Util;
  336. }
  337. return exports;
  338. }(util);
  339. events = function (exports) {
  340. var Util = util;
  341. // Returns a function that will be executed at most one time, no matter how
  342. // often you call it. Useful for lazy initialization.
  343. var _once = function (func) {
  344. var ran = false, memo;
  345. return function () {
  346. if (ran)
  347. return memo;
  348. ran = true;
  349. memo = func.apply(this, arguments);
  350. func = null;
  351. return memo;
  352. };
  353. };
  354. /**
  355. * @discription events
  356. * @mixin
  357. */
  358. var Events = {
  359. // Bind an event to a `callback` function. Passing `"all"` will bind
  360. // the callback to all events fired.
  361. on: function (name, callback, context) {
  362. if (!eventsApi(this, 'on', name, [
  363. callback,
  364. context
  365. ]) || !callback)
  366. return this;
  367. this._events || (this._events = {});
  368. var events = this._events[name] || (this._events[name] = []);
  369. events.push({
  370. callback: callback,
  371. context: context,
  372. ctx: context || this
  373. });
  374. return this;
  375. },
  376. // Bind an event to only be triggered a single time. After the first time
  377. // the callback is invoked, it will be removed.
  378. once: function (name, callback, context) {
  379. if (!eventsApi(this, 'once', name, [
  380. callback,
  381. context
  382. ]) || !callback)
  383. return this;
  384. var self = this;
  385. var once = _once(function () {
  386. self.off(name, once);
  387. callback.apply(this, arguments);
  388. });
  389. once._callback = callback;
  390. return this.on(name, once, context);
  391. },
  392. // Remove one or many callbacks. If `context` is null, removes all
  393. // callbacks with that function. If `callback` is null, removes all
  394. // callbacks for the event. If `name` is null, removes all bound
  395. // callbacks for all events.
  396. off: function (name, callback, context) {
  397. if (!this._events || !eventsApi(this, 'off', name, [
  398. callback,
  399. context
  400. ]))
  401. return this;
  402. // Remove all callbacks for all events.
  403. if (!name && !callback && !context) {
  404. this._events = void 0;
  405. return this;
  406. }
  407. var names = name ? [name] : Object.keys(this._events);
  408. for (var i = 0, length = names.length; i < length; i++) {
  409. name = names[i];
  410. // Bail out if there are no events stored.
  411. var events = this._events[name];
  412. if (!events)
  413. continue;
  414. // Remove all callbacks for this event.
  415. if (!callback && !context) {
  416. delete this._events[name];
  417. continue;
  418. }
  419. // Find any remaining events.
  420. var remaining = [];
  421. for (var j = 0, k = events.length; j < k; j++) {
  422. var event = events[j];
  423. if (callback && callback !== event.callback && callback !== event.callback._callback || context && context !== event.context) {
  424. remaining.push(event);
  425. }
  426. }
  427. // Replace events if there are any remaining. Otherwise, clean up.
  428. if (remaining.length) {
  429. this._events[name] = remaining;
  430. } else {
  431. delete this._events[name];
  432. }
  433. }
  434. return this;
  435. },
  436. // Trigger one or many events, firing all bound callbacks. Callbacks are
  437. // passed the same arguments as `trigger` is, apart from the event name
  438. // (unless you're listening on `"all"`, which will cause your callback to
  439. // receive the true name of the event as the first argument).
  440. trigger: function (name) {
  441. if (!this._events)
  442. return this;
  443. var args = Array.prototype.slice.call(arguments, 1);
  444. if (!eventsApi(this, 'trigger', name, args))
  445. return this;
  446. var events = this._events[name];
  447. var allEvents = this._events.all;
  448. if (events)
  449. triggerEvents(events, args);
  450. if (allEvents)
  451. triggerEvents(allEvents, arguments);
  452. return this;
  453. },
  454. // Inversion-of-control versions of `on` and `once`. Tell *this* object to
  455. // listen to an event in another object ... keeping track of what it's
  456. // listening to.
  457. listenTo: function (obj, name, callback) {
  458. var listeningTo = this._listeningTo || (this._listeningTo = {});
  459. var id = obj._listenId || (obj._listenId = Util.guid('l'));
  460. listeningTo[id] = obj;
  461. if (!callback && typeof name === 'object')
  462. callback = this;
  463. obj.on(name, callback, this);
  464. return this;
  465. },
  466. listenToOnce: function (obj, name, callback) {
  467. if (typeof name === 'object') {
  468. for (var event in name)
  469. this.listenToOnce(obj, event, name[event]);
  470. return this;
  471. }
  472. var cb = _once(function () {
  473. this.stopListening(obj, name, cb);
  474. callback.apply(this, arguments);
  475. });
  476. cb._callback = callback;
  477. return this.listenTo(obj, name, cb);
  478. },
  479. // Tell this object to stop listening to either specific events ... or
  480. // to every object it's currently listening to.
  481. stopListening: function (obj, name, callback) {
  482. var listeningTo = this._listeningTo;
  483. if (!listeningTo)
  484. return this;
  485. var remove = !name && !callback;
  486. if (!callback && typeof name === 'object')
  487. callback = this;
  488. if (obj)
  489. (listeningTo = {})[obj._listenId] = obj;
  490. for (var id in listeningTo) {
  491. obj = listeningTo[id];
  492. obj.off(name, callback, this);
  493. if (remove || Util.isEmpty(obj._events))
  494. delete this._listeningTo[id];
  495. }
  496. return this;
  497. }
  498. };
  499. // Regular expression used to split event strings.
  500. var eventSplitter = /\s+/;
  501. // Implement fancy features of the Events API such as multiple event
  502. // names `"change blur"` and jQuery-style event maps `{change: action}`
  503. // in terms of the existing API.
  504. var eventsApi = function (obj, action, name, rest) {
  505. if (!name)
  506. return true;
  507. // Handle event maps.
  508. if (typeof name === 'object') {
  509. for (var key in name) {
  510. obj[action].apply(obj, [
  511. key,
  512. name[key]
  513. ].concat(rest));
  514. }
  515. return false;
  516. }
  517. // Handle space separated event names.
  518. if (eventSplitter.test(name)) {
  519. var names = name.split(eventSplitter);
  520. for (var i = 0, length = names.length; i < length; i++) {
  521. obj[action].apply(obj, [names[i]].concat(rest));
  522. }
  523. return false;
  524. }
  525. return true;
  526. };
  527. // A difficult-to-believe, but optimized internal dispatch function for
  528. // triggering events. Tries to keep the usual cases speedy (most internal
  529. var triggerEvents = function (events, args) {
  530. var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
  531. switch (args.length) {
  532. case 0:
  533. while (++i < l)
  534. (ev = events[i]).callback.call(ev.ctx);
  535. return;
  536. case 1:
  537. while (++i < l)
  538. (ev = events[i]).callback.call(ev.ctx, a1);
  539. return;
  540. case 2:
  541. while (++i < l)
  542. (ev = events[i]).callback.call(ev.ctx, a1, a2);
  543. return;
  544. case 3:
  545. while (++i < l)
  546. (ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
  547. return;
  548. default:
  549. while (++i < l)
  550. (ev = events[i]).callback.apply(ev.ctx, args);
  551. return;
  552. }
  553. };
  554. // Aliases for backwards compatibility.
  555. Events.bind = Events.on;
  556. Events.unbind = Events.off;
  557. if (typeof module == 'object' && module.exports) {
  558. exports = Events;
  559. } /** ignored by jsdoc **/ else {
  560. return Events;
  561. }
  562. return exports;
  563. }(events);
  564. base = function (exports) {
  565. var Util = util;
  566. var Events = events;
  567. /**
  568. @constructor
  569. @mixes Events
  570. */
  571. var Base = function () {
  572. };
  573. Util.mix(Base.prototype, Events);
  574. Util.mix(Base.prototype, {
  575. /**
  576. * @memberof Base
  577. * @param {object} plugin plug a plugin
  578. */
  579. plug: function (plugin) {
  580. var self = this;
  581. if (!plugin || !plugin.pluginId)
  582. return;
  583. if (!self.__plugins) {
  584. self.__plugins = [];
  585. }
  586. var __plugin = self.getPlugin(plugin.pluginId);
  587. __plugin && self.unplug(plugin.pluginId);
  588. plugin.pluginInitializer(self);
  589. self.__plugins.push(plugin);
  590. return self;
  591. },
  592. /**
  593. * @memberof Base
  594. * @param {object|string} plugin unplug a plugin by pluginId or plugin instance
  595. */
  596. unplug: function (plugin) {
  597. var self = this;
  598. if (!plugin || !self.__plugins)
  599. return;
  600. var _plugin = typeof plugin == 'string' ? self.getPlugin(plugin) : plugin;
  601. _plugin.pluginDestructor(self);
  602. for (var i = 0, l = self.__plugins.length; i < l; i++) {
  603. if (self.__plugins[i] == _plugin) {
  604. return self.__plugins.splice(i, 1);
  605. }
  606. }
  607. },
  608. /**
  609. * @memberof Base
  610. * @param {object|string} plugin get plugin by pluginId
  611. */
  612. getPlugin: function (pluginId) {
  613. var self = this;
  614. var plugins = [];
  615. if (!self.__plugins)
  616. return;
  617. for (var i = 0, l = self.__plugins.length; i < l; i++) {
  618. if (self.__plugins[i] && self.__plugins[i].pluginId == pluginId) {
  619. plugins.push(self.__plugins[i]);
  620. }
  621. }
  622. return plugins.length > 1 ? plugins : plugins[0] || null;
  623. }
  624. });
  625. if (typeof module == 'object' && module.exports) {
  626. exports = Base;
  627. } /** ignored by jsdoc **/ else {
  628. return Base;
  629. }
  630. return exports;
  631. }(base);
  632. easing = function (exports) {
  633. //easing
  634. var Easing = {
  635. 'linear': [
  636. 0,
  637. 0,
  638. 1,
  639. 1
  640. ],
  641. 'ease': [
  642. 0.25,
  643. 0.1,
  644. 0.25,
  645. 1
  646. ],
  647. 'ease-in': [
  648. 0.42,
  649. 0,
  650. 1,
  651. 1
  652. ],
  653. 'ease-out': [
  654. 0,
  655. 0,
  656. 0.58,
  657. 1
  658. ],
  659. 'ease-in-out': [
  660. 0.42,
  661. 0,
  662. 0.58,
  663. 1
  664. ],
  665. 'quadratic': [
  666. 0.33,
  667. 0.66,
  668. 0.66,
  669. 1
  670. ],
  671. 'circular': [
  672. 0.1,
  673. 0.57,
  674. 0.1,
  675. 1
  676. ],
  677. 'bounce': [
  678. 0.71,
  679. 1.35,
  680. 0.47,
  681. 1.41
  682. ],
  683. format: function (easing) {
  684. if (!easing)
  685. return;
  686. if (typeof easing === 'string' && this[easing]) {
  687. return this[easing] instanceof Array ? [
  688. ' cubic-bezier(',
  689. this[easing],
  690. ') '
  691. ].join('') : this[easing];
  692. }
  693. if (easing instanceof Array) {
  694. return [
  695. ' cubic-bezier(',
  696. easing,
  697. ') '
  698. ].join('');
  699. }
  700. return easing;
  701. }
  702. };
  703. if (typeof module == 'object' && module.exports) {
  704. exports = Easing;
  705. } /** ignored by jsdoc **/ else {
  706. return Easing;
  707. }
  708. return exports;
  709. }(easing);
  710. timer = function (exports) {
  711. var Util = util;
  712. var Base = base;
  713. var Easing = easing;
  714. var RAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
  715. window.setTimeout(callback, 1000 / 60);
  716. };
  717. var vendors = [
  718. 'webkit',
  719. 'moz',
  720. 'ms',
  721. 'o'
  722. ];
  723. var cancelRAF = window.cancelAnimationFrame;
  724. if (!cancelRAF) {
  725. for (var i = 0; i < vendors.length; i++) {
  726. if (window[vendors[i] + 'CancelAnimationFrame'] || window[vendors[i] + 'CancelRequestAnimationFrame']) {
  727. cancelRAF = window[vendors[i] + 'CancelAnimationFrame'] || window[vendors[i] + 'CancelRequestAnimationFrame'];
  728. }
  729. }
  730. }
  731. cancelRAF = cancelRAF || window.clearTimeout;
  732. function Bezier(x1, y1, x2, y2, epsilon) {
  733. var curveX = function (t) {
  734. var v = 1 - t;
  735. return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
  736. };
  737. var curveY = function (t) {
  738. var v = 1 - t;
  739. return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
  740. };
  741. var derivativeCurveX = function (t) {
  742. var v = 1 - t;
  743. return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2;
  744. };
  745. return function (t) {
  746. var x = t, t0, t1, t2, x2, d2, i;
  747. // First try a few iterations of Newton's method -- normally very fast.
  748. for (t2 = x, i = 0; i < 8; i++) {
  749. x2 = curveX(t2) - x;
  750. if (Math.abs(x2) < epsilon)
  751. return curveY(t2);
  752. d2 = derivativeCurveX(t2);
  753. if (Math.abs(d2) < 0.000001)
  754. break;
  755. t2 = t2 - x2 / d2;
  756. }
  757. t0 = 0, t1 = 1, t2 = x;
  758. if (t2 < t0)
  759. return curveY(t0);
  760. if (t2 > t1)
  761. return curveY(t1);
  762. // Fallback to the bisection method for reliability.
  763. while (t0 < t1) {
  764. x2 = curveX(t2);
  765. if (Math.abs(x2 - x) < epsilon)
  766. return curveY(t2);
  767. if (x > x2)
  768. t0 = t2;
  769. else
  770. t1 = t2;
  771. t2 = (t1 - t0) * 0.5 + t0;
  772. }
  773. // Failure
  774. return curveY(t2);
  775. };
  776. }
  777. function Timer(cfg) {
  778. var self = this;
  779. self.cfg = Util.mix({ easing: 'linear' }, cfg);
  780. }
  781. Timer.MIN_DURATION = 1;
  782. Util.extend(Timer, Base, {
  783. reset: function (cfg) {
  784. var self = this;
  785. Util.mix(self.cfg, cfg);
  786. self.isfinished = false;
  787. self.percent = 0;
  788. self._stop = null;
  789. },
  790. run: function () {
  791. var self = this;
  792. var duration = self.cfg.duration;
  793. if (duration <= Timer.MIN_DURATION) {
  794. self.isfinished = true;
  795. self.trigger('run', { percent: 1 });
  796. self.trigger('end', { percent: 1 });
  797. }
  798. if (self.isfinished)
  799. return;
  800. self._hasFinishedPercent = self._stop && self._stop.percent || 0;
  801. self._stop = null;
  802. self.start = Date.now();
  803. self.percent = 0;
  804. // epsilon determines the precision of the solved values
  805. var epsilon = 1000 / 60 / duration / 4;
  806. var b = Easing[self.cfg.easing];
  807. self.easingFn = Bezier(b[0], b[1], b[2], b[3], epsilon);
  808. self._run();
  809. },
  810. _run: function () {
  811. var self = this;
  812. cancelRAF(self._raf);
  813. self._raf = RAF(function () {
  814. self.now = Date.now();
  815. self.duration = self.now - self.start >= self.cfg.duration ? self.cfg.duration : self.now - self.start;
  816. self.progress = self.easingFn(self.duration / self.cfg.duration);
  817. self.percent = self.duration / self.cfg.duration + self._hasFinishedPercent;
  818. if (self.percent >= 1 || self._stop) {
  819. self.percent = self._stop && self._stop.percent ? self._stop.percent : 1;
  820. self.duration = self._stop && self._stop.duration ? self._stop.duration : self.duration;
  821. var param = { percent: self.percent };
  822. self.trigger('stop', param);
  823. if (self.percent >= 1) {
  824. self.isfinished = true;
  825. self.trigger('end', { percent: 1 });
  826. }
  827. return;
  828. }
  829. self.trigger('run', {
  830. percent: self.progress,
  831. originPercent: self.percent
  832. });
  833. self._run();
  834. });
  835. },
  836. stop: function () {
  837. var self = this;
  838. self._stop = {
  839. percent: self.percent,
  840. now: self.now
  841. };
  842. cancelRAF(self._raf);
  843. }
  844. });
  845. if (typeof module == 'object' && module.exports) {
  846. exports = Timer;
  847. } /** ignored by jsdoc **/ else {
  848. return Timer;
  849. }
  850. return exports;
  851. }(timer);
  852. animate = function (exports) {
  853. var Util = util;
  854. var Timer = timer;
  855. var Easing = easing;
  856. var Base = base;
  857. //transform
  858. var vendorTransform = Util.prefixStyle('transform');
  859. //transition webkitTransition MozTransition OTransition msTtransition
  860. var vendorTransition = Util.prefixStyle('transition');
  861. var vendorTransitionDuration = Util.prefixStyle('transitionDuration');
  862. var vendorTransformOrigin = Util.prefixStyle('transformOrigin');
  863. var vendorTransitionEnd = Util.vendor ? Util.prefixStyle('transitionEnd') : 'transitionend';
  864. var vendorTransformStr = Util.vendor ? [
  865. '-',
  866. Util.vendor,
  867. '-transform'
  868. ].join('') : 'transform';
  869. var translateTpl = 'translateX({translateX}px) translateY({translateY}px) translateZ(0)';
  870. //limit attrs
  871. var animAttrs = {
  872. 'transform': true,
  873. 'opacity': true,
  874. 'scrollTop': true,
  875. 'scrollLeft': true
  876. };
  877. function myParse(v) {
  878. return Math.round(parseFloat(v) * 100000) / 100000;
  879. }
  880. function defaultDecompose() {
  881. return {
  882. translateX: 0,
  883. translateY: 0,
  884. rotate: 0,
  885. skewX: 0,
  886. skewY: 0,
  887. scaleX: 1,
  888. scaleY: 1
  889. };
  890. }
  891. function toMatrixArray(matrix) {
  892. matrix = matrix.split(/,/);
  893. matrix = Array.prototype.map.call(matrix, function (v) {
  894. return myParse(v);
  895. });
  896. return matrix;
  897. }
  898. function decomposeMatrix(matrix) {
  899. matrix = toMatrixArray(matrix);
  900. var scaleX, scaleY, skew, A = matrix[0], B = matrix[1], C = matrix[2], D = matrix[3];
  901. // Make sure matrix is not singular
  902. if (A * D - B * C) {
  903. scaleX = Math.sqrt(A * A + B * B);
  904. skew = (A * C + B * D) / (A * D - C * B);
  905. scaleY = (A * D - B * C) / scaleX;
  906. // step (6)
  907. if (A * D < B * C) {
  908. skew = -skew;
  909. scaleX = -scaleX;
  910. } // matrix is singular and cannot be interpolated
  911. } else {
  912. // In this case the elem shouldn't be rendered, hence scale == 0
  913. scaleX = scaleY = skew = 0;
  914. }
  915. // The recomposition order is very important
  916. // see http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971
  917. return {
  918. translateX: myParse(matrix[4]),
  919. translateY: myParse(matrix[5]),
  920. rotate: myParse(Math.atan2(B, A) * 180 / Math.PI),
  921. skewX: myParse(Math.atan(skew) * 180 / Math.PI),
  922. skewY: 0,
  923. scaleX: myParse(scaleX),
  924. scaleY: myParse(scaleY)
  925. };
  926. }
  927. function getTransformInfo(transform) {
  928. transform = transform.split(')');
  929. var trim = Util.trim, i = -1, l = transform.length - 1, split, prop, val, ret = defaultDecompose();
  930. // Loop through the transform properties, parse and multiply them
  931. while (++i < l) {
  932. split = transform[i].split('(');
  933. prop = trim(split[0]);
  934. val = split[1];
  935. switch (prop) {
  936. case 'translateX':
  937. case 'translateY':
  938. case 'scaleX':
  939. case 'scaleY':
  940. ret[prop] = myParse(val);
  941. break;
  942. case 'translate':
  943. case 'translate3d':
  944. val = val.split(',');
  945. ret.translateX = myParse(val[0]);
  946. ret.translateY = myParse(val[1] || 0);
  947. break;
  948. case 'scale':
  949. val = val.split(',');
  950. ret.scaleX = myParse(val[0]);
  951. ret.scaleY = myParse(val[1] || val[0]);
  952. break;
  953. case 'matrix':
  954. return decomposeMatrix(val);
  955. }
  956. }
  957. return ret;
  958. }
  959. /**
  960. * animate function
  961. * @constructor
  962. * @param {HTMLElement} el element to animate
  963. * @param {Object} config config for animate
  964. * @param {Object} config.css
  965. * @param {Number} config.duration
  966. * @param {String} config.easing
  967. * @extends {Base}
  968. */
  969. function Animate(el, cfg) {
  970. if (!el || !cfg || !cfg.css)
  971. return;
  972. var self = this;
  973. self.cfg = cfg;
  974. self.el = el;
  975. var duration = cfg.duration || 0, easing = cfg.easing || 'ease', delay = cfg.delay || 0;
  976. //trigger run
  977. if (cfg.run) {
  978. //frame animate
  979. self.timer = self.timer || new Timer({
  980. duration: Math.round(duration),
  981. easing: easing
  982. });
  983. self.timer.on('run', cfg.run);
  984. }
  985. self._bindEvt();
  986. return self;
  987. }
  988. function computeTransform(prevTransform, destTransform) {
  989. var transform = getTransformInfo(prevTransform);
  990. var dest = getTransformInfo(destTransform);
  991. var trans = {};
  992. for (var i in dest) {
  993. trans[i] = {
  994. prevVal: transform[i],
  995. newVal: dest[i]
  996. };
  997. }
  998. return trans;
  999. }
  1000. //for scroll only
  1001. function setStyle(el, styleName, prevVal, newVal, percent) {
  1002. prevVal = isNaN(Number(prevVal)) ? 0 : Number(prevVal);
  1003. var curVal = (newVal - prevVal) * percent + prevVal;
  1004. css(el, styleName, curVal);
  1005. }
  1006. function css(el, styleName, val) {
  1007. switch (styleName) {
  1008. case 'scrollTop':
  1009. case 'scrollLeft':
  1010. el[styleName] = val;
  1011. break;
  1012. case 'transform':
  1013. el.style[vendorTransform] = val;
  1014. case 'opacity':
  1015. el.style[styleName] = val;
  1016. break;
  1017. }
  1018. }
  1019. Util.extend(Animate, Base, {
  1020. /**
  1021. * to start the animation
  1022. * @memberof Animate
  1023. * @return {Animate}
  1024. */
  1025. run: function () {
  1026. var self = this;
  1027. var cfg = self.cfg, el = self.el, duration = cfg.duration || 0, easing = cfg.easing || 'ease', delay = cfg.delay || 0;
  1028. self.__isTransitionEnd = false;
  1029. clearTimeout(self.__itv);
  1030. self.timer && self.timer.run();
  1031. if (duration <= Timer.MIN_DURATION) {
  1032. for (var i in cfg.css) {
  1033. css(el, i, cfg.css[i]);
  1034. }
  1035. self.stop();
  1036. self.__handlers.stop.call(self);
  1037. return;
  1038. }
  1039. if (Util.isBadAndroid()) {
  1040. //use frame animate on bad android device
  1041. cfg.useTransition = false;
  1042. }
  1043. if (cfg.useTransition) {
  1044. //transition
  1045. el.style[vendorTransition] = Util.substitute('all {duration}ms {easing} {delay}ms', {
  1046. duration: Math.round(duration),
  1047. easing: Easing.format(easing),
  1048. delay: delay
  1049. });
  1050. for (var i in cfg.css) {
  1051. //set css
  1052. css(el, i, cfg.css[i]);
  1053. }
  1054. self.__itv = setTimeout(function () {
  1055. if (!self.__isTransitionEnd) {
  1056. self.__isTransitionEnd = true;
  1057. self.trigger('transitionend');
  1058. }
  1059. }, Number(duration) + 60);
  1060. } else {
  1061. self.computeStyle = self.computeStyle || window.getComputedStyle(el);
  1062. //transform
  1063. if (cfg.css.transform && self.timer) {
  1064. var transmap = self.transmap = computeTransform(self.computeStyle[vendorTransform], cfg.css.transform);
  1065. self.timer.off('run', self.__handlers.transRun);
  1066. self.timer.on('run', self.__handlers.transRun, self);
  1067. self.timer.off('end', self.__handlers.transRun);
  1068. self.timer.on('end', self.__handlers.transRun, self);
  1069. }
  1070. }
  1071. return self;
  1072. },
  1073. _transitionEndHandler: function (e) {
  1074. var self = this;
  1075. self.stop();
  1076. self.__handlers.stop.call(self);
  1077. },
  1078. __handlers: {
  1079. transRun: function (e) {
  1080. var self = this;
  1081. var transmap = self.transmap;
  1082. var el = self.el;
  1083. var newTrans = {};
  1084. for (var i in transmap) {
  1085. newTrans[i] = (transmap[i].newVal - transmap[i].prevVal) * e.percent + transmap[i].prevVal;
  1086. }
  1087. var ret = Util.substitute(translateTpl + ' ' + 'scale({scaleX},{scaleY})', newTrans);
  1088. el.style[vendorTransform] = ret;
  1089. },
  1090. stop: function (e) {
  1091. var self = this;
  1092. var cfg = self.cfg;
  1093. cfg.end && cfg.end({ percent: 1 });
  1094. }
  1095. },
  1096. _bindEvt: function () {
  1097. var self = this;
  1098. var cfg = self.cfg;
  1099. var el = self.el;
  1100. self.el.addEventListener(vendorTransitionEnd, function (e) {
  1101. self.__isTransitionEnd = true;
  1102. if (e.target !== e.currentTarget)
  1103. return;
  1104. self.trigger('transitionend', e);
  1105. });
  1106. self.on('transitionend', self._transitionEndHandler, self);
  1107. var cssRun = function (e) {
  1108. self.computeStyle = self.computeStyle || window.getComputedStyle(el);
  1109. for (var i in cfg.css) {
  1110. if (!/transform/.test(i)) {
  1111. setStyle(self.el, i, self.computeStyle[i], cfg.css[i], e.percent);
  1112. }
  1113. }
  1114. };
  1115. self.timer && self.timer.on('run', cssRun);
  1116. self.timer && self.timer.on('stop', self.__handlers.stop, self);
  1117. },
  1118. /**
  1119. * to stop the animation
  1120. * @memberof Animate
  1121. * @return {Animate}
  1122. */
  1123. stop: function () {
  1124. var self = this;
  1125. if (self.cfg.useTransition && self.cfg.duration > Timer.MIN_DURATION) {
  1126. var computeStyle = window.getComputedStyle(this.el);
  1127. for (var i in self.cfg.css) {
  1128. if (animAttrs[i]) {
  1129. var value = /transform/.test(i) ? computeStyle[vendorTransform] : computeStyle[i];
  1130. css(self.el, i, Util.substitute(translateTpl + ' ' + 'scale({scaleX},{scaleY})', getTransformInfo(value)));
  1131. }
  1132. }
  1133. self.el.style[vendorTransition] = 'none';
  1134. }
  1135. self.timer && self.timer.stop() && self.timer.reset();
  1136. self.computeStyle = null;
  1137. return self;
  1138. },
  1139. /**
  1140. * to reset the animation to a new state
  1141. * @memberof Animate
  1142. * @param {object} cfg cfg for new animation
  1143. * @return {Animate}
  1144. */
  1145. reset: function (cfg) {
  1146. var self = this;
  1147. self.computeStyle = null;
  1148. Util.mix(self.cfg, cfg);
  1149. this.timer && self.timer.reset({
  1150. duration: Math.round(self.cfg.duration),
  1151. easing: self.cfg.easing
  1152. });
  1153. return self;
  1154. }
  1155. });
  1156. if (typeof module == 'object' && module.exports) {
  1157. exports = Animate;
  1158. } /** ignored by jsdoc **/ else {
  1159. return Animate;
  1160. }
  1161. return exports;
  1162. }(animate);
  1163. hammer = function (exports) {
  1164. var VENDOR_PREFIXES = [
  1165. '',
  1166. 'webkit',
  1167. 'moz',
  1168. 'MS',
  1169. 'ms',
  1170. 'o'
  1171. ];
  1172. var TEST_ELEMENT = document.createElement('div');
  1173. var TYPE_FUNCTION = 'function';
  1174. var round = Math.round;
  1175. var abs = Math.abs;
  1176. var now = Date.now;
  1177. /**
  1178. * set a timeout with a given scope
  1179. * @param {Function} fn
  1180. * @param {Number} timeout
  1181. * @param {Object} context
  1182. * @returns {number}
  1183. */
  1184. function setTimeoutContext(fn, timeout, context) {
  1185. return setTimeout(bindFn(fn, context), timeout);
  1186. }
  1187. /**
  1188. * if the argument is an array, we want to execute the fn on each entry
  1189. * if it aint an array we don't want to do a thing.
  1190. * this is used by all the methods that accept a single and array argument.
  1191. * @param {*|Array} arg
  1192. * @param {String} fn
  1193. * @param {Object} [context]
  1194. * @returns {Boolean}
  1195. */
  1196. function invokeArrayArg(arg, fn, context) {
  1197. if (Array.isArray(arg)) {
  1198. each(arg, context[fn], context);
  1199. return true;
  1200. }
  1201. return false;
  1202. }
  1203. /**
  1204. * walk objects and arrays
  1205. * @param {Object} obj
  1206. * @param {Function} iterator
  1207. * @param {Object} context
  1208. */
  1209. function each(obj, iterator, context) {
  1210. var i;
  1211. if (!obj) {
  1212. return;
  1213. }
  1214. if (obj.forEach) {
  1215. obj.forEach(iterator, context);
  1216. } else if (obj.length !== undefined) {
  1217. i = 0;
  1218. while (i < obj.length) {
  1219. iterator.call(context, obj[i], i, obj);
  1220. i++;
  1221. }
  1222. } else {
  1223. for (i in obj) {
  1224. obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
  1225. }
  1226. }
  1227. }
  1228. /**
  1229. * extend object.
  1230. * means that properties in dest will be overwritten by the ones in src.
  1231. * @param {Object} dest
  1232. * @param {Object} src
  1233. * @param {Boolean} [merge]
  1234. * @returns {Object} dest
  1235. */
  1236. function extend(dest, src, merge) {
  1237. var keys = Object.keys(src);
  1238. var i = 0;
  1239. while (i < keys.length) {
  1240. if (!merge || merge && dest[keys[i]] === undefined) {
  1241. dest[keys[i]] = src[keys[i]];
  1242. }
  1243. i++;
  1244. }
  1245. return dest;
  1246. }
  1247. /**
  1248. * merge the values from src in the dest.
  1249. * means that properties that exist in dest will not be overwritten by src
  1250. * @param {Object} dest
  1251. * @param {Object} src
  1252. * @returns {Object} dest
  1253. */
  1254. function merge(dest, src) {
  1255. return extend(dest, src, true);
  1256. }
  1257. /**
  1258. * simple class inheritance
  1259. * @param {Function} child
  1260. * @param {Function} base
  1261. * @param {Object} [properties]
  1262. */
  1263. function inherit(child, base, properties) {
  1264. var baseP = base.prototype, childP;
  1265. childP = child.prototype = Object.create(baseP);
  1266. childP.constructor = child;
  1267. childP._super = baseP;
  1268. if (properties) {
  1269. extend(childP, properties);
  1270. }
  1271. }
  1272. /**
  1273. * simple function bind
  1274. * @param {Function} fn
  1275. * @param {Object} context
  1276. * @returns {Function}
  1277. */
  1278. function bindFn(fn, context) {
  1279. return function boundFn() {
  1280. return fn.apply(context, arguments);
  1281. };
  1282. }
  1283. /**
  1284. * let a boolean value also be a function that must return a boolean
  1285. * this first item in args will be used as the context
  1286. * @param {Boolean|Function} val
  1287. * @param {Array} [args]
  1288. * @returns {Boolean}
  1289. */
  1290. function boolOrFn(val, args) {
  1291. if (typeof val == TYPE_FUNCTION) {
  1292. return val.apply(args ? args[0] || undefined : undefined, args);
  1293. }
  1294. return val;
  1295. }
  1296. /**
  1297. * use the val2 when val1 is undefined
  1298. * @param {*} val1
  1299. * @param {*} val2
  1300. * @returns {*}
  1301. */
  1302. function ifUndefined(val1, val2) {
  1303. return val1 === undefined ? val2 : val1;
  1304. }
  1305. /**
  1306. * addEventListener with multiple events at once
  1307. * @param {EventTarget} target
  1308. * @param {String} types
  1309. * @param {Function} handler
  1310. */
  1311. function addEventListeners(target, types, handler) {
  1312. each(splitStr(types), function (type) {
  1313. target.addEventListener(type, handler, false);
  1314. });
  1315. }
  1316. /**
  1317. * removeEventListener with multiple events at once
  1318. * @param {EventTarget} target
  1319. * @param {String} types
  1320. * @param {Function} handler
  1321. */
  1322. function removeEventListeners(target, types, handler) {
  1323. each(splitStr(types), function (type) {
  1324. target.removeEventListener(type, handler, false);
  1325. });
  1326. }
  1327. /**
  1328. * find if a node is in the given parent
  1329. * @method hasParent
  1330. * @param {HTMLElement} node
  1331. * @param {HTMLElement} parent
  1332. * @return {Boolean} found
  1333. */
  1334. function hasParent(node, parent) {
  1335. while (node) {
  1336. if (node == parent) {
  1337. return true;
  1338. }
  1339. node = node.parentNode;
  1340. }
  1341. return false;
  1342. }
  1343. /**
  1344. * small indexOf wrapper
  1345. * @param {String} str
  1346. * @param {String} find
  1347. * @returns {Boolean} found
  1348. */
  1349. function inStr(str, find) {
  1350. return str.indexOf(find) > -1;
  1351. }
  1352. /**
  1353. * split string on whitespace
  1354. * @param {String} str
  1355. * @returns {Array} words
  1356. */
  1357. function splitStr(str) {
  1358. return str.trim().split(/\s+/g);
  1359. }
  1360. /**
  1361. * find if a array contains the object using indexOf or a simple polyFill
  1362. * @param {Array} src
  1363. * @param {String} find
  1364. * @param {String} [findByKey]
  1365. * @return {Boolean|Number} false when not found, or the index
  1366. */
  1367. function inArray(src, find, findByKey) {
  1368. if (src.indexOf && !findByKey) {
  1369. return src.indexOf(find);
  1370. } else {
  1371. var i = 0;
  1372. while (i < src.length) {
  1373. if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) {
  1374. return i;
  1375. }
  1376. i++;
  1377. }
  1378. return -1;
  1379. }
  1380. }
  1381. /**
  1382. * convert array-like objects to real arrays
  1383. * @param {Object} obj
  1384. * @returns {Array}
  1385. */
  1386. function toArray(obj) {
  1387. return Array.prototype.slice.call(obj, 0);
  1388. }
  1389. /**
  1390. * unique array with objects based on a key (like 'id') or just by the array's value
  1391. * @param {Array} src [{id:1},{id:2},{id:1}]
  1392. * @param {String} [key]
  1393. * @param {Boolean} [sort=False]
  1394. * @returns {Array} [{id:1},{id:2}]
  1395. */
  1396. function uniqueArray(src, key, sort) {
  1397. var results = [];
  1398. var values = [];
  1399. var i = 0;
  1400. while (i < src.length) {
  1401. var val = key ? src[i][key] : src[i];
  1402. if (inArray(values, val) < 0) {
  1403. results.push(src[i]);
  1404. }
  1405. values[i] = val;
  1406. i++;
  1407. }
  1408. if (sort) {
  1409. if (!key) {
  1410. results = results.sort();
  1411. } else {
  1412. results = results.sort(function sortUniqueArray(a, b) {
  1413. return a[key] > b[key];
  1414. });
  1415. }
  1416. }
  1417. return results;
  1418. }
  1419. /**
  1420. * get the prefixed property
  1421. * @param {Object} obj
  1422. * @param {String} property
  1423. * @returns {String|Undefined} prefixed
  1424. */
  1425. function prefixed(obj, property) {
  1426. var prefix, prop;
  1427. var camelProp = property[0].toUpperCase() + property.slice(1);
  1428. var i = 0;
  1429. while (i < VENDOR_PREFIXES.length) {
  1430. prefix = VENDOR_PREFIXES[i];
  1431. prop = prefix ? prefix + camelProp : property;
  1432. if (prop in obj) {
  1433. return prop;
  1434. }
  1435. i++;
  1436. }
  1437. return undefined;
  1438. }
  1439. /**
  1440. * get a unique id
  1441. * @returns {number} uniqueId
  1442. */
  1443. var _uniqueId = 1;
  1444. function uniqueId() {
  1445. return _uniqueId++;
  1446. }
  1447. /**
  1448. * get the window object of an element
  1449. * @param {HTMLElement} element
  1450. * @returns {DocumentView|Window}
  1451. */
  1452. function getWindowForElement(element) {
  1453. var doc = element.ownerDocument;
  1454. return doc.defaultView || doc.parentWindow;
  1455. }
  1456. var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
  1457. var SUPPORT_TOUCH = 'ontouchstart' in window;
  1458. var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
  1459. var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
  1460. var INPUT_TYPE_TOUCH = 'touch';
  1461. var INPUT_TYPE_PEN = 'pen';
  1462. var INPUT_TYPE_MOUSE = 'mouse';
  1463. var INPUT_TYPE_KINECT = 'kinect';
  1464. var COMPUTE_INTERVAL = 25;
  1465. var INPUT_START = 1;
  1466. var INPUT_MOVE = 2;
  1467. var INPUT_END = 4;
  1468. var INPUT_CANCEL = 8;
  1469. var DIRECTION_NONE = 1;
  1470. var DIRECTION_LEFT = 2;
  1471. var DIRECTION_RIGHT = 4;
  1472. var DIRECTION_UP = 8;
  1473. var DIRECTION_DOWN = 16;
  1474. var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
  1475. var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
  1476. var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
  1477. var PROPS_XY = [
  1478. 'x',
  1479. 'y'
  1480. ];
  1481. var PROPS_CLIENT_XY = [
  1482. 'clientX',
  1483. 'clientY'
  1484. ];
  1485. /**
  1486. * create new input type manager
  1487. * @param {Manager} manager
  1488. * @param {Function} callback
  1489. * @returns {Input}
  1490. * @constructor
  1491. */
  1492. function Input(manager, callback) {
  1493. var self = this;
  1494. this.manager = manager;
  1495. this.callback = callback;
  1496. this.element = manager.element;
  1497. this.target = manager.options.inputTarget;
  1498. // smaller wrapper around the handler, for the scope and the enabled state of the manager,
  1499. // so when disabled the input events are completely bypassed.
  1500. this.domHandler = function (ev) {
  1501. if (boolOrFn(manager.options.enable, [manager])) {
  1502. self.handler(ev);
  1503. }
  1504. };
  1505. this.init();
  1506. }
  1507. Input.prototype = {
  1508. /**
  1509. * should handle the inputEvent data and trigger the callback
  1510. * @virtual
  1511. */
  1512. handler: function () {
  1513. },
  1514. /**
  1515. * bind the events
  1516. */
  1517. init: function () {
  1518. this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
  1519. this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
  1520. this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  1521. },
  1522. /**
  1523. * unbind the events
  1524. */
  1525. destroy: function () {
  1526. this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
  1527. this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
  1528. this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  1529. }
  1530. };
  1531. /**
  1532. * create new input type manager
  1533. * called by the Manager constructor
  1534. * @param {Hammer} manager
  1535. * @returns {Input}
  1536. */
  1537. function createInputInstance(manager) {
  1538. var Type;
  1539. var inputClass = manager.options.inputClass;
  1540. if (inputClass) {
  1541. Type = inputClass;
  1542. } else if (SUPPORT_POINTER_EVENTS) {
  1543. Type = PointerEventInput;
  1544. } else if (SUPPORT_ONLY_TOUCH) {
  1545. Type = TouchInput;
  1546. } else if (!SUPPORT_TOUCH) {
  1547. Type = MouseInput;
  1548. } else {
  1549. Type = TouchMouseInput;
  1550. }
  1551. return new Type(manager, inputHandler);
  1552. }
  1553. /**
  1554. * handle input events
  1555. * @param {Manager} manager
  1556. * @param {String} eventType
  1557. * @param {Object} input
  1558. */
  1559. function inputHandler(manager, eventType, input) {
  1560. var pointersLen = input.pointers.length;
  1561. var changedPointersLen = input.changedPointers.length;
  1562. var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0;
  1563. var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0;
  1564. input.isFirst = !!isFirst;
  1565. input.isFinal = !!isFinal;
  1566. if (isFirst) {
  1567. manager.session = {};
  1568. }
  1569. // source event is the normalized value of the domEvents
  1570. // like 'touchstart, mouseup, pointerdown'
  1571. input.eventType = eventType;
  1572. // compute scale, rotation etc
  1573. computeInputData(manager, input);
  1574. // emit secret event
  1575. manager.emit('hammer.input', input);
  1576. manager.recognize(input);
  1577. manager.session.prevInput = input;
  1578. }
  1579. /**
  1580. * extend the data with some usable properties like scale, rotate, velocity etc
  1581. * @param {Object} manager
  1582. * @param {Object} input
  1583. */
  1584. function computeInputData(manager, input) {
  1585. var session = manager.session;
  1586. var pointers = input.pointers;
  1587. var pointersLength = pointers.length;
  1588. // store the first input to calculate the distance and direction
  1589. if (!session.firstInput) {
  1590. session.firstInput = simpleCloneInputData(input);
  1591. }
  1592. // to compute scale and rotation we need to store the multiple touches
  1593. if (pointersLength > 1 && !session.firstMultiple) {
  1594. session.firstMultiple = simpleCloneInputData(input);
  1595. } else if (pointersLength === 1) {
  1596. session.firstMultiple = false;
  1597. }
  1598. var firstInput = session.firstInput;
  1599. var firstMultiple = session.firstMultiple;
  1600. var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
  1601. var center = input.center = getCenter(pointers);
  1602. input.timeStamp = now();
  1603. input.deltaTime = input.timeStamp - firstInput.timeStamp;
  1604. input.angle = getAngle(offsetCenter, center);
  1605. input.distance = getDistance(offsetCenter, center);
  1606. computeDeltaXY(session, input);
  1607. input.offsetDirection = getDirection(input.deltaX, input.deltaY);
  1608. input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
  1609. input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
  1610. computeIntervalInputData(session, input);
  1611. // find the correct target
  1612. var target = manager.element;
  1613. if (hasParent(input.srcEvent.target, target)) {
  1614. target = input.srcEvent.target;
  1615. }
  1616. input.target = target;
  1617. }
  1618. function computeDeltaXY(session, input) {
  1619. var center = input.center;
  1620. var offset = session.offsetDelta || {};
  1621. var prevDelta = session.prevDelta || {};
  1622. var prevInput = session.prevInput || {};
  1623. if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
  1624. prevDelta = session.prevDelta = {
  1625. x: prevInput.deltaX || 0,
  1626. y: prevInput.deltaY || 0
  1627. };
  1628. offset = session.offsetDelta = {
  1629. x: center.x,
  1630. y: center.y
  1631. };
  1632. }
  1633. input.deltaX = prevDelta.x + (center.x - offset.x);
  1634. input.deltaY = prevDelta.y + (center.y - offset.y);
  1635. }
  1636. /**
  1637. * velocity is calculated every x ms
  1638. * @param {Object} session
  1639. * @param {Object} input
  1640. */
  1641. function computeIntervalInputData(session, input) {
  1642. var last = session.lastInterval || input, deltaTime = input.timeStamp - last.timeStamp, velocity, velocityX, velocityY, direction;
  1643. if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
  1644. var deltaX = last.deltaX - input.deltaX;
  1645. var deltaY = last.deltaY - input.deltaY;
  1646. var v = getVelocity(deltaTime, deltaX, deltaY);
  1647. velocityX = v.x;
  1648. velocityY = v.y;
  1649. velocity = abs(v.x) > abs(v.y) ? v.x : v.y;
  1650. direction = getDirection(deltaX, deltaY);
  1651. session.lastInterval = input;
  1652. } else {
  1653. // use latest velocity info if it doesn't overtake a minimum period
  1654. velocity = last.velocity;
  1655. velocityX = last.velocityX;
  1656. velocityY = last.velocityY;
  1657. direction = last.direction;
  1658. }
  1659. input.velocity = velocity;
  1660. input.velocityX = velocityX;
  1661. input.velocityY = velocityY;
  1662. input.direction = direction;
  1663. }
  1664. /**
  1665. * create a simple clone from the input used for storage of firstInput and firstMultiple
  1666. * @param {Object} input
  1667. * @returns {Object} clonedInputData
  1668. */
  1669. function simpleCloneInputData(input) {
  1670. // make a simple copy of the pointers because we will get a reference if we don't
  1671. // we only need clientXY for the calculations
  1672. var pointers = [];
  1673. var i = 0;
  1674. while (i < input.pointers.length) {
  1675. pointers[i] = {
  1676. clientX: round(input.pointers[i].clientX),
  1677. clientY: round(input.pointers[i].clientY)
  1678. };
  1679. i++;
  1680. }
  1681. return {
  1682. timeStamp: now(),
  1683. pointers: pointers,
  1684. center: getCenter(pointers),
  1685. deltaX: input.deltaX,
  1686. deltaY: input.deltaY
  1687. };
  1688. }
  1689. /**
  1690. * get the center of all the pointers
  1691. * @param {Array} pointers
  1692. * @return {Object} center contains `x` and `y` properties
  1693. */
  1694. function getCenter(pointers) {
  1695. var pointersLength = pointers.length;
  1696. // no need to loop when only one touch
  1697. if (pointersLength === 1) {
  1698. return {
  1699. x: round(pointers[0].clientX),
  1700. y: round(pointers[0].clientY)
  1701. };
  1702. }
  1703. var x = 0, y = 0, i = 0;
  1704. while (i < pointersLength) {
  1705. x += pointers[i].clientX;
  1706. y += pointers[i].clientY;
  1707. i++;
  1708. }
  1709. return {
  1710. x: round(x / pointersLength),
  1711. y: round(y / pointersLength)
  1712. };
  1713. }
  1714. /**
  1715. * calculate the velocity between two points. unit is in px per ms.
  1716. * @param {Number} deltaTime
  1717. * @param {Number} x
  1718. * @param {Number} y
  1719. * @return {Object} velocity `x` and `y`
  1720. */
  1721. function getVelocity(deltaTime, x, y) {
  1722. return {
  1723. x: x / deltaTime || 0,
  1724. y: y / deltaTime || 0
  1725. };
  1726. }
  1727. /**
  1728. * get the direction between two points
  1729. * @param {Number} x
  1730. * @param {Number} y
  1731. * @return {Number} direction
  1732. */
  1733. function getDirection(x, y) {
  1734. if (x === y) {
  1735. return DIRECTION_NONE;
  1736. }
  1737. if (abs(x) >= abs(y)) {
  1738. return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  1739. }
  1740. return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
  1741. }
  1742. /**
  1743. * calculate the absolute distance between two points
  1744. * @param {Object} p1 {x, y}
  1745. * @param {Object} p2 {x, y}
  1746. * @param {Array} [props] containing x and y keys
  1747. * @return {Number} distance
  1748. */
  1749. function getDistance(p1, p2, props) {
  1750. if (!props) {
  1751. props = PROPS_XY;
  1752. }
  1753. var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
  1754. return Math.sqrt(x * x + y * y);
  1755. }
  1756. /**
  1757. * calculate the angle between two coordinates
  1758. * @param {Object} p1
  1759. * @param {Object} p2
  1760. * @param {Array} [props] containing x and y keys
  1761. * @return {Number} angle
  1762. */
  1763. function getAngle(p1, p2, props) {
  1764. if (!props) {
  1765. props = PROPS_XY;
  1766. }
  1767. var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
  1768. return Math.atan2(y, x) * 180 / Math.PI;
  1769. }
  1770. /**
  1771. * calculate the rotation degrees between two pointersets
  1772. * @param {Array} start array of pointers
  1773. * @param {Array} end array of pointers
  1774. * @return {Number} rotation
  1775. */
  1776. function getRotation(start, end) {
  1777. return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
  1778. }
  1779. /**
  1780. * calculate the scale factor between two pointersets
  1781. * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
  1782. * @param {Array} start array of pointers
  1783. * @param {Array} end array of pointers
  1784. * @return {Number} scale
  1785. */
  1786. function getScale(start, end) {
  1787. return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
  1788. }
  1789. var MOUSE_INPUT_MAP = {
  1790. mousedown: INPUT_START,
  1791. mousemove: INPUT_MOVE,
  1792. mouseup: INPUT_END
  1793. };
  1794. var MOUSE_ELEMENT_EVENTS = 'mousedown';
  1795. var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
  1796. /**
  1797. * Mouse events input
  1798. * @constructor
  1799. * @extends Input
  1800. */
  1801. function MouseInput() {
  1802. this.evEl = MOUSE_ELEMENT_EVENTS;
  1803. this.evWin = MOUSE_WINDOW_EVENTS;
  1804. this.allow = true;
  1805. // used by Input.TouchMouse to disable mouse events
  1806. this.pressed = false;
  1807. // mousedown state
  1808. Input.apply(this, arguments);
  1809. }
  1810. inherit(MouseInput, Input, {
  1811. /**
  1812. * handle mouse events
  1813. * @param {Object} ev
  1814. */
  1815. handler: function MEhandler(ev) {
  1816. var eventType = MOUSE_INPUT_MAP[ev.type];
  1817. // on start we want to have the left mouse button down
  1818. if (eventType & INPUT_START && ev.button === 0) {
  1819. this.pressed = true;
  1820. }
  1821. if (eventType & INPUT_MOVE && ev.which !== 1) {
  1822. eventType = INPUT_END;
  1823. }
  1824. // mouse must be down, and mouse events are allowed (see the TouchMouse input)
  1825. if (!this.pressed || !this.allow) {
  1826. return;
  1827. }
  1828. if (eventType & INPUT_END) {
  1829. this.pressed = false;
  1830. }
  1831. this.callback(this.manager, eventType, {
  1832. pointers: [ev],
  1833. changedPointers: [ev],
  1834. pointerType: INPUT_TYPE_MOUSE,
  1835. srcEvent: ev
  1836. });
  1837. }
  1838. });
  1839. var POINTER_INPUT_MAP = {
  1840. pointerdown: INPUT_START,
  1841. pointermove: INPUT_MOVE,
  1842. pointerup: INPUT_END,
  1843. pointercancel: INPUT_CANCEL,
  1844. pointerout: INPUT_CANCEL
  1845. };
  1846. // in IE10 the pointer types is defined as an enum
  1847. var IE10_POINTER_TYPE_ENUM = {
  1848. 2: INPUT_TYPE_TOUCH,
  1849. 3: INPUT_TYPE_PEN,
  1850. 4: INPUT_TYPE_MOUSE,
  1851. 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
  1852. };
  1853. var POINTER_ELEMENT_EVENTS = 'pointerdown';
  1854. var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
  1855. // IE10 has prefixed support, and case-sensitive
  1856. if (window.MSPointerEvent) {
  1857. POINTER_ELEMENT_EVENTS = 'MSPointerDown';
  1858. POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
  1859. }
  1860. /**
  1861. * Pointer events input
  1862. * @constructor
  1863. * @extends Input
  1864. */
  1865. function PointerEventInput() {
  1866. this.evEl = POINTER_ELEMENT_EVENTS;
  1867. this.evWin = POINTER_WINDOW_EVENTS;
  1868. Input.apply(this, arguments);
  1869. this.store = this.manager.session.pointerEvents = [];
  1870. }
  1871. inherit(PointerEventInput, Input, {
  1872. /**
  1873. * handle mouse events
  1874. * @param {Object} ev
  1875. */
  1876. handler: function PEhandler(ev) {
  1877. var store = this.store;
  1878. var removePointer = false;
  1879. var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
  1880. var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
  1881. var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
  1882. var isTouch = pointerType == INPUT_TYPE_TOUCH;
  1883. // get index of the event in the store
  1884. var storeIndex = inArray(store, ev.pointerId, 'pointerId');
  1885. // start and mouse must be down
  1886. if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
  1887. if (storeIndex < 0) {
  1888. store.push(ev);
  1889. storeIndex = store.length - 1;
  1890. }
  1891. } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
  1892. removePointer = true;
  1893. }
  1894. // it not found, so the pointer hasn't been down (so it's probably a hover)
  1895. if (storeIndex < 0) {
  1896. return;
  1897. }
  1898. // update the event in the store
  1899. store[storeIndex] = ev;
  1900. this.callback(this.manager, eventType, {
  1901. pointers: store,
  1902. changedPointers: [ev],
  1903. pointerType: pointerType,
  1904. srcEvent: ev
  1905. });
  1906. if (removePointer) {
  1907. // remove from the store
  1908. store.splice(storeIndex, 1);
  1909. }
  1910. }
  1911. });
  1912. var SINGLE_TOUCH_INPUT_MAP = {
  1913. touchstart: INPUT_START,
  1914. touchmove: INPUT_MOVE,
  1915. touchend: INPUT_END,
  1916. touchcancel: INPUT_CANCEL
  1917. };
  1918. var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
  1919. var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
  1920. /**
  1921. * Touch events input
  1922. * @constructor
  1923. * @extends Input
  1924. */
  1925. function SingleTouchInput() {
  1926. this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
  1927. this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
  1928. this.started = false;
  1929. Input.apply(this, arguments);
  1930. }
  1931. inherit(SingleTouchInput, Input, {
  1932. handler: function TEhandler(ev) {
  1933. var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
  1934. // should we handle the touch events?
  1935. if (type === INPUT_START) {
  1936. this.started = true;
  1937. }
  1938. if (!this.started) {
  1939. return;
  1940. }
  1941. var touches = normalizeSingleTouches.call(this, ev, type);
  1942. // when done, reset the started state
  1943. if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
  1944. this.started = false;
  1945. }
  1946. this.callback(this.manager, type, {
  1947. pointers: touches[0],
  1948. changedPointers: touches[1],
  1949. pointerType: INPUT_TYPE_TOUCH,
  1950. srcEvent: ev
  1951. });
  1952. }
  1953. });
  1954. /**
  1955. * @this {TouchInput}
  1956. * @param {Object} ev
  1957. * @param {Number} type flag
  1958. * @returns {undefined|Array} [all, changed]
  1959. */
  1960. function normalizeSingleTouches(ev, type) {
  1961. var all = toArray(ev.touches);
  1962. var changed = toArray(ev.changedTouches);
  1963. if (type & (INPUT_END | INPUT_CANCEL)) {
  1964. all = uniqueArray(all.concat(changed), 'identifier', true);
  1965. }
  1966. return [
  1967. all,
  1968. changed
  1969. ];
  1970. }
  1971. var TOUCH_INPUT_MAP = {
  1972. touchstart: INPUT_START,
  1973. touchmove: INPUT_MOVE,
  1974. touchend: INPUT_END,
  1975. touchcancel: INPUT_CANCEL
  1976. };
  1977. var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
  1978. /**
  1979. * Multi-user touch events input
  1980. * @constructor
  1981. * @extends Input
  1982. */
  1983. function TouchInput() {
  1984. this.evTarget = TOUCH_TARGET_EVENTS;
  1985. this.targetIds = {};
  1986. Input.apply(this, arguments);
  1987. }
  1988. inherit(TouchInput, Input, {
  1989. handler: function MTEhandler(ev) {
  1990. var type = TOUCH_INPUT_MAP[ev.type];
  1991. var touches = getTouches.call(this, ev, type);
  1992. if (!touches) {
  1993. return;
  1994. }
  1995. this.callback(this.manager, type, {
  1996. pointers: touches[0],
  1997. changedPointers: touches[1],
  1998. pointerType: INPUT_TYPE_TOUCH,
  1999. srcEvent: ev
  2000. });
  2001. }
  2002. });
  2003. /**
  2004. * @this {TouchInput}
  2005. * @param {Object} ev
  2006. * @param {Number} type flag
  2007. * @returns {undefined|Array} [all, changed]
  2008. */
  2009. function getTouches(ev, type) {
  2010. var allTouches = toArray(ev.touches);
  2011. var targetIds = this.targetIds;
  2012. // when there is only one touch, the process can be simplified
  2013. if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
  2014. targetIds[allTouches[0].identifier] = true;
  2015. return [
  2016. allTouches,
  2017. allTouches
  2018. ];
  2019. }
  2020. var i, targetTouches, changedTouches = toArray(ev.changedTouches), changedTargetTouches = [], target = this.target;
  2021. // get target touches from touches
  2022. targetTouches = allTouches.filter(function (touch) {
  2023. return hasParent(touch.target, target);
  2024. });
  2025. // collect touches
  2026. if (type === INPUT_START) {
  2027. i = 0;
  2028. while (i < targetTouches.length) {
  2029. targetIds[targetTouches[i].identifier] = true;
  2030. i++;
  2031. }
  2032. }
  2033. // filter changed touches to only contain touches that exist in the collected target ids
  2034. i = 0;
  2035. while (i < changedTouches.length) {
  2036. if (targetIds[changedTouches[i].identifier]) {
  2037. changedTargetTouches.push(changedTouches[i]);
  2038. }
  2039. // cleanup removed touches
  2040. if (type & (INPUT_END | INPUT_CANCEL)) {
  2041. delete targetIds[changedTouches[i].identifier];
  2042. }
  2043. i++;
  2044. }
  2045. if (!changedTargetTouches.length) {
  2046. return;
  2047. }
  2048. return [
  2049. // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
  2050. uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
  2051. changedTargetTouches
  2052. ];
  2053. }
  2054. /**
  2055. * Combined touch and mouse input
  2056. *
  2057. * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
  2058. * This because touch devices also emit mouse events while doing a touch.
  2059. *
  2060. * @constructor
  2061. * @extends Input
  2062. */
  2063. function TouchMouseInput() {
  2064. Input.apply(this, arguments);
  2065. var handler = bindFn(this.handler, this);
  2066. this.touch = new TouchInput(this.manager, handler);
  2067. this.mouse = new MouseInput(this.manager, handler);
  2068. }
  2069. inherit(TouchMouseInput, Input, {
  2070. /**
  2071. * handle mouse and touch events
  2072. * @param {Hammer} manager
  2073. * @param {String} inputEvent
  2074. * @param {Object} inputData
  2075. */
  2076. handler: function TMEhandler(manager, inputEvent, inputData) {
  2077. var isTouch = inputData.pointerType == INPUT_TYPE_TOUCH, isMouse = inputData.pointerType == INPUT_TYPE_MOUSE;
  2078. // when we're in a touch event, so block all upcoming mouse events
  2079. // most mobile browser also emit mouseevents, right after touchstart
  2080. if (isTouch) {
  2081. this.mouse.allow = false;
  2082. } else if (isMouse && !this.mouse.allow) {
  2083. return;
  2084. }
  2085. // reset the allowMouse when we're done
  2086. if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
  2087. this.mouse.allow = true;
  2088. }
  2089. this.callback(manager, inputEvent, inputData);
  2090. },
  2091. /**
  2092. * remove the event listeners
  2093. */
  2094. destroy: function destroy() {
  2095. this.touch.destroy();
  2096. this.mouse.destroy();
  2097. }
  2098. });
  2099. var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
  2100. var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
  2101. // magical touchAction value
  2102. var TOUCH_ACTION_COMPUTE = 'compute';
  2103. var TOUCH_ACTION_AUTO = 'auto';
  2104. var TOUCH_ACTION_MANIPULATION = 'manipulation';
  2105. // not implemented
  2106. var TOUCH_ACTION_NONE = 'none';
  2107. var TOUCH_ACTION_PAN_X = 'pan-x';
  2108. var TOUCH_ACTION_PAN_Y = 'pan-y';
  2109. /**
  2110. * Touch Action
  2111. * sets the touchAction property or uses the js alternative
  2112. * @param {Manager} manager
  2113. * @param {String} value
  2114. * @constructor
  2115. */
  2116. function TouchAction(manager, value) {
  2117. this.manager = manager;
  2118. this.set(value);
  2119. }
  2120. TouchAction.prototype = {
  2121. /**
  2122. * set the touchAction value on the element or enable the polyfill
  2123. * @param {String} value
  2124. */
  2125. set: function (value) {
  2126. // find out the touch-action by the event handlers
  2127. if (value == TOUCH_ACTION_COMPUTE) {
  2128. value = this.compute();
  2129. }
  2130. if (NATIVE_TOUCH_ACTION) {
  2131. this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
  2132. }
  2133. this.actions = value.toLowerCase().trim();
  2134. },
  2135. /**
  2136. * just re-set the touchAction value
  2137. */
  2138. update: function () {
  2139. this.set(this.manager.options.touchAction);
  2140. },
  2141. /**
  2142. * compute the value for the touchAction property based on the recognizer's settings
  2143. * @returns {String} value
  2144. */
  2145. compute: function () {
  2146. var actions = [];
  2147. each(this.manager.recognizers, function (recognizer) {
  2148. if (boolOrFn(recognizer.options.enable, [recognizer])) {
  2149. actions = actions.concat(recognizer.getTouchAction());
  2150. }
  2151. });
  2152. return cleanTouchActions(actions.join(' '));
  2153. },
  2154. /**
  2155. * this method is called on each input cycle and provides the preventing of the browser behavior
  2156. * @param {Object} input
  2157. */
  2158. preventDefaults: function (input) {
  2159. // not needed with native support for the touchAction property
  2160. if (NATIVE_TOUCH_ACTION) {
  2161. return;
  2162. }
  2163. var srcEvent = input.srcEvent;
  2164. var direction = input.offsetDirection;
  2165. // if the touch action did prevented once this session
  2166. if (this.manager.session.prevented) {
  2167. srcEvent.preventDefault();
  2168. return;
  2169. }
  2170. var actions = this.actions;
  2171. var hasNone = inStr(actions, TOUCH_ACTION_NONE);
  2172. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
  2173. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
  2174. if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) {
  2175. return this.preventSrc(srcEvent);
  2176. }
  2177. },
  2178. /**
  2179. * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
  2180. * @param {Object} srcEvent
  2181. */
  2182. preventSrc: function (srcEvent) {
  2183. this.manager.session.prevented = true;
  2184. srcEvent.preventDefault();
  2185. }
  2186. };
  2187. /**
  2188. * when the touchActions are collected they are not a valid value, so we need to clean things up. *
  2189. * @param {String} actions
  2190. * @returns {*}
  2191. */
  2192. function cleanTouchActions(actions) {
  2193. // none
  2194. if (inStr(actions, TOUCH_ACTION_NONE)) {
  2195. return TOUCH_ACTION_NONE;
  2196. }
  2197. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
  2198. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
  2199. // pan-x and pan-y can be combined
  2200. if (hasPanX && hasPanY) {
  2201. return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
  2202. }
  2203. // pan-x OR pan-y
  2204. if (hasPanX || hasPanY) {
  2205. return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
  2206. }
  2207. // manipulation
  2208. if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
  2209. return TOUCH_ACTION_MANIPULATION;
  2210. }
  2211. return TOUCH_ACTION_AUTO;
  2212. }
  2213. /**
  2214. * Recognizer flow explained; *
  2215. * All recognizers have the initial state of POSSIBLE when a input session starts.
  2216. * The definition of a input session is from the first input until the last input, with all it's movement in it. *
  2217. * Example session for mouse-input: mousedown -> mousemove -> mouseup
  2218. *
  2219. * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
  2220. * which determines with state it should be.
  2221. *
  2222. * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
  2223. * POSSIBLE to give it another change on the next cycle.
  2224. *
  2225. * Possible
  2226. * |
  2227. * +-----+---------------+
  2228. * | |
  2229. * +-----+-----+ |
  2230. * | | |
  2231. * Failed Cancelled |
  2232. * +-------+------+
  2233. * | |
  2234. * Recognized Began
  2235. * |
  2236. * Changed
  2237. * |
  2238. * Ended/Recognized
  2239. */
  2240. var STATE_POSSIBLE = 1;
  2241. var STATE_BEGAN = 2;
  2242. var STATE_CHANGED = 4;
  2243. var STATE_ENDED = 8;
  2244. var STATE_RECOGNIZED = STATE_ENDED;
  2245. var STATE_CANCELLED = 16;
  2246. var STATE_FAILED = 32;
  2247. /**
  2248. * Recognizer
  2249. * Every recognizer needs to extend from this class.
  2250. * @constructor
  2251. * @param {Object} options
  2252. */
  2253. function Recognizer(options) {
  2254. this.id = uniqueId();
  2255. this.manager = null;
  2256. this.options = merge(options || {}, this.defaults);
  2257. // default is enable true
  2258. this.options.enable = ifUndefined(this.options.enable, true);
  2259. this.state = STATE_POSSIBLE;
  2260. this.simultaneous = {};
  2261. this.requireFail = [];
  2262. }
  2263. Recognizer.prototype = {
  2264. /**
  2265. * @virtual
  2266. * @type {Object}
  2267. */
  2268. defaults: {},
  2269. /**
  2270. * set options
  2271. * @param {Object} options
  2272. * @return {Recognizer}
  2273. */
  2274. set: function (options) {
  2275. extend(this.options, options);
  2276. // also update the touchAction, in case something changed about the directions/enabled state
  2277. this.manager && this.manager.touchAction.update();
  2278. return this;
  2279. },
  2280. /**
  2281. * recognize simultaneous with an other recognizer.
  2282. * @param {Recognizer} otherRecognizer
  2283. * @returns {Recognizer} this
  2284. */
  2285. recognizeWith: function (otherRecognizer) {
  2286. if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
  2287. return this;
  2288. }
  2289. var simultaneous = this.simultaneous;
  2290. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2291. if (!simultaneous[otherRecognizer.id]) {
  2292. simultaneous[otherRecognizer.id] = otherRecognizer;
  2293. otherRecognizer.recognizeWith(this);
  2294. }
  2295. return this;
  2296. },
  2297. /**
  2298. * drop the simultaneous link. it doesnt remove the link on the other recognizer.
  2299. * @param {Recognizer} otherRecognizer
  2300. * @returns {Recognizer} this
  2301. */
  2302. dropRecognizeWith: function (otherRecognizer) {
  2303. if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
  2304. return this;
  2305. }
  2306. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2307. delete this.simultaneous[otherRecognizer.id];
  2308. return this;
  2309. },
  2310. /**
  2311. * recognizer can only run when an other is failing
  2312. * @param {Recognizer} otherRecognizer
  2313. * @returns {Recognizer} this
  2314. */
  2315. requireFailure: function (otherRecognizer) {
  2316. if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
  2317. return this;
  2318. }
  2319. var requireFail = this.requireFail;
  2320. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2321. if (inArray(requireFail, otherRecognizer) === -1) {
  2322. requireFail.push(otherRecognizer);
  2323. otherRecognizer.requireFailure(this);
  2324. }
  2325. return this;
  2326. },
  2327. /**
  2328. * drop the requireFailure link. it does not remove the link on the other recognizer.
  2329. * @param {Recognizer} otherRecognizer
  2330. * @returns {Recognizer} this
  2331. */
  2332. dropRequireFailure: function (otherRecognizer) {
  2333. if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
  2334. return this;
  2335. }
  2336. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2337. var index = inArray(this.requireFail, otherRecognizer);
  2338. if (index > -1) {
  2339. this.requireFail.splice(index, 1);
  2340. }
  2341. return this;
  2342. },
  2343. /**
  2344. * has require failures boolean
  2345. * @returns {boolean}
  2346. */
  2347. hasRequireFailures: function () {
  2348. return this.requireFail.length > 0;
  2349. },
  2350. /**
  2351. * if the recognizer can recognize simultaneous with an other recognizer
  2352. * @param {Recognizer} otherRecognizer
  2353. * @returns {Boolean}
  2354. */
  2355. canRecognizeWith: function (otherRecognizer) {
  2356. return !!this.simultaneous[otherRecognizer.id];
  2357. },
  2358. /**
  2359. * You should use `tryEmit` instead of `emit` directly to check
  2360. * that all the needed recognizers has failed before emitting.
  2361. * @param {Object} input
  2362. */
  2363. emit: function (input) {
  2364. var self = this;
  2365. var state = this.state;
  2366. function emit(withState) {
  2367. self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input);
  2368. }
  2369. // 'panstart' and 'panmove'
  2370. if (state < STATE_ENDED) {
  2371. emit(true);
  2372. }
  2373. emit();
  2374. // simple 'eventName' events
  2375. // panend and pancancel
  2376. if (state >= STATE_ENDED) {
  2377. emit(true);
  2378. }
  2379. },
  2380. /**
  2381. * Check that all the require failure recognizers has failed,
  2382. * if true, it emits a gesture event,
  2383. * otherwise, setup the state to FAILED.
  2384. * @param {Object} input
  2385. */
  2386. tryEmit: function (input) {
  2387. if (this.canEmit()) {
  2388. return this.emit(input);
  2389. }
  2390. // it's failing anyway
  2391. this.state = STATE_FAILED;
  2392. },
  2393. /**
  2394. * can we emit?
  2395. * @returns {boolean}
  2396. */
  2397. canEmit: function () {
  2398. var i = 0;
  2399. while (i < this.requireFail.length) {
  2400. if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
  2401. return false;
  2402. }
  2403. i++;
  2404. }
  2405. return true;
  2406. },
  2407. /**
  2408. * update the recognizer
  2409. * @param {Object} inputData
  2410. */
  2411. recognize: function (inputData) {
  2412. // make a new copy of the inputData
  2413. // so we can change the inputData without messing up the other recognizers
  2414. var inputDataClone = extend({}, inputData);
  2415. // is is enabled and allow recognizing?
  2416. if (!boolOrFn(this.options.enable, [
  2417. this,
  2418. inputDataClone
  2419. ])) {
  2420. this.reset();
  2421. this.state = STATE_FAILED;
  2422. return;
  2423. }
  2424. // reset when we've reached the end
  2425. if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
  2426. this.state = STATE_POSSIBLE;
  2427. }
  2428. this.state = this.process(inputDataClone);
  2429. // the recognizer has recognized a gesture
  2430. // so trigger an event
  2431. if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
  2432. this.tryEmit(inputDataClone);
  2433. }
  2434. },
  2435. /**
  2436. * return the state of the recognizer
  2437. * the actual recognizing happens in this method
  2438. * @virtual
  2439. * @param {Object} inputData
  2440. * @returns {Const} STATE
  2441. */
  2442. process: function (inputData) {
  2443. },
  2444. // jshint ignore:line
  2445. /**
  2446. * return the preferred touch-action
  2447. * @virtual
  2448. * @returns {Array}
  2449. */
  2450. getTouchAction: function () {
  2451. },
  2452. /**
  2453. * called when the gesture isn't allowed to recognize
  2454. * like when another is being recognized or it is disabled
  2455. * @virtual
  2456. */
  2457. reset: function () {
  2458. }
  2459. };
  2460. /**
  2461. * get a usable string, used as event postfix
  2462. * @param {Const} state
  2463. * @returns {String} state
  2464. */
  2465. function stateStr(state) {
  2466. if (state & STATE_CANCELLED) {
  2467. return 'cancel';
  2468. } else if (state & STATE_ENDED) {
  2469. return 'end';
  2470. } else if (state & STATE_CHANGED) {
  2471. return 'move';
  2472. } else if (state & STATE_BEGAN) {
  2473. return 'start';
  2474. }
  2475. return '';
  2476. }
  2477. /**
  2478. * direction cons to string
  2479. * @param {Const} direction
  2480. * @returns {String}
  2481. */
  2482. function directionStr(direction) {
  2483. if (direction == DIRECTION_DOWN) {
  2484. return 'down';
  2485. } else if (direction == DIRECTION_UP) {
  2486. return 'up';
  2487. } else if (direction == DIRECTION_LEFT) {
  2488. return 'left';
  2489. } else if (direction == DIRECTION_RIGHT) {
  2490. return 'right';
  2491. }
  2492. return '';
  2493. }
  2494. /**
  2495. * get a recognizer by name if it is bound to a manager
  2496. * @param {Recognizer|String} otherRecognizer
  2497. * @param {Recognizer} recognizer
  2498. * @returns {Recognizer}
  2499. */
  2500. function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
  2501. var manager = recognizer.manager;
  2502. if (manager) {
  2503. return manager.get(otherRecognizer);
  2504. }
  2505. return otherRecognizer;
  2506. }
  2507. /**
  2508. * This recognizer is just used as a base for the simple attribute recognizers.
  2509. * @constructor
  2510. * @extends Recognizer
  2511. */
  2512. function AttrRecognizer() {
  2513. Recognizer.apply(this, arguments);
  2514. }
  2515. inherit(AttrRecognizer, Recognizer, {
  2516. /**
  2517. * @namespace
  2518. * @memberof AttrRecognizer
  2519. */
  2520. defaults: {
  2521. /**
  2522. * @type {Number}
  2523. * @default 1
  2524. */
  2525. pointers: 1
  2526. },
  2527. /**
  2528. * Used to check if it the recognizer receives valid input, like input.distance > 10.
  2529. * @memberof AttrRecognizer
  2530. * @param {Object} input
  2531. * @returns {Boolean} recognized
  2532. */
  2533. attrTest: function (input) {
  2534. var optionPointers = this.options.pointers;
  2535. return optionPointers === 0 || input.pointers.length === optionPointers;
  2536. },
  2537. /**
  2538. * Process the input and return the state for the recognizer
  2539. * @memberof AttrRecognizer
  2540. * @param {Object} input
  2541. * @returns {*} State
  2542. */
  2543. process: function (input) {
  2544. var state = this.state;
  2545. var eventType = input.eventType;
  2546. var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
  2547. var isValid = this.attrTest(input);
  2548. // on cancel input and we've recognized before, return STATE_CANCELLED
  2549. if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
  2550. return state | STATE_CANCELLED;
  2551. } else if (isRecognized || isValid) {
  2552. if (eventType & INPUT_END) {
  2553. return state | STATE_ENDED;
  2554. } else if (!(state & STATE_BEGAN)) {
  2555. return STATE_BEGAN;
  2556. }
  2557. return state | STATE_CHANGED;
  2558. }
  2559. return STATE_FAILED;
  2560. }
  2561. });
  2562. /**
  2563. * Pan
  2564. * Recognized when the pointer is down and moved in the allowed direction.
  2565. * @constructor
  2566. * @extends AttrRecognizer
  2567. */
  2568. function PanRecognizer() {
  2569. AttrRecognizer.apply(this, arguments);
  2570. this.pX = null;
  2571. this.pY = null;
  2572. }
  2573. inherit(PanRecognizer, AttrRecognizer, {
  2574. /**
  2575. * @namespace
  2576. * @memberof PanRecognizer
  2577. */
  2578. defaults: {
  2579. event: 'pan',
  2580. threshold: 10,
  2581. pointers: 1,
  2582. direction: DIRECTION_ALL
  2583. },
  2584. getTouchAction: function () {
  2585. var direction = this.options.direction;
  2586. var actions = [];
  2587. if (direction & DIRECTION_HORIZONTAL) {
  2588. actions.push(TOUCH_ACTION_PAN_Y);
  2589. }
  2590. if (direction & DIRECTION_VERTICAL) {
  2591. actions.push(TOUCH_ACTION_PAN_X);
  2592. }
  2593. return actions;
  2594. },
  2595. directionTest: function (input) {
  2596. var options = this.options;
  2597. var hasMoved = true;
  2598. var distance = input.distance;
  2599. var direction = input.direction;
  2600. var x = input.deltaX;
  2601. var y = input.deltaY;
  2602. // lock to axis?
  2603. if (!(direction & options.direction)) {
  2604. if (options.direction & DIRECTION_HORIZONTAL) {
  2605. direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  2606. hasMoved = x != this.pX;
  2607. distance = Math.abs(input.deltaX);
  2608. } else {
  2609. direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
  2610. hasMoved = y != this.pY;
  2611. distance = Math.abs(input.deltaY);
  2612. }
  2613. }
  2614. input.direction = direction;
  2615. return hasMoved && distance > options.threshold && direction & options.direction;
  2616. },
  2617. attrTest: function (input) {
  2618. return AttrRecognizer.prototype.attrTest.call(this, input) && (this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input));
  2619. },
  2620. emit: function (input) {
  2621. this.pX = input.deltaX;
  2622. this.pY = input.deltaY;
  2623. var direction = directionStr(input.direction);
  2624. if (direction) {
  2625. this.manager.emit(this.options.event + direction, input);
  2626. }
  2627. this._super.emit.call(this, input);
  2628. },
  2629. reset: function () {
  2630. }
  2631. });
  2632. /**
  2633. * Pinch
  2634. * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
  2635. * @constructor
  2636. * @extends AttrRecognizer
  2637. */
  2638. function PinchRecognizer() {
  2639. AttrRecognizer.apply(this, arguments);
  2640. }
  2641. inherit(PinchRecognizer, AttrRecognizer, {
  2642. /**
  2643. * @namespace
  2644. * @memberof PinchRecognizer
  2645. */
  2646. defaults: {
  2647. event: 'pinch',
  2648. threshold: 0,
  2649. pointers: 2
  2650. },
  2651. getTouchAction: function () {
  2652. return [TOUCH_ACTION_NONE];
  2653. },
  2654. attrTest: function (input) {
  2655. return this._super.attrTest.call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
  2656. },
  2657. emit: function (input) {
  2658. this._super.emit.call(this, input);
  2659. if (input.scale !== 1) {
  2660. var inOut = input.scale < 1 ? 'in' : 'out';
  2661. this.manager.emit(this.options.event + inOut, input);
  2662. }
  2663. }
  2664. });
  2665. /**
  2666. * Press
  2667. * Recognized when the pointer is down for x ms without any movement.
  2668. * @constructor
  2669. * @extends Recognizer
  2670. */
  2671. function PressRecognizer() {
  2672. Recognizer.apply(this, arguments);
  2673. this._timer = null;
  2674. this._input = null;
  2675. }
  2676. inherit(PressRecognizer, Recognizer, {
  2677. /**
  2678. * @namespace
  2679. * @memberof PressRecognizer
  2680. */
  2681. defaults: {
  2682. event: 'press',
  2683. pointers: 1,
  2684. time: 500,
  2685. // minimal time of the pointer to be pressed
  2686. threshold: 5 // a minimal movement is ok, but keep it low
  2687. },
  2688. getTouchAction: function () {
  2689. return [TOUCH_ACTION_AUTO];
  2690. },
  2691. process: function (input) {
  2692. var options = this.options;
  2693. var validPointers = input.pointers.length === options.pointers;
  2694. var validMovement = input.distance < options.threshold;
  2695. var validTime = input.deltaTime > options.time;
  2696. this._input = input;
  2697. // we only allow little movement
  2698. // and we've reached an end event, so a tap is possible
  2699. if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) {
  2700. this.reset();
  2701. } else if (input.eventType & INPUT_START) {
  2702. this.reset();
  2703. this._timer = setTimeoutContext(function () {
  2704. this.state = STATE_RECOGNIZED;
  2705. this.tryEmit();
  2706. }, options.time, this);
  2707. } else if (input.eventType & INPUT_END) {
  2708. return STATE_RECOGNIZED;
  2709. }
  2710. return STATE_FAILED;
  2711. },
  2712. reset: function () {
  2713. clearTimeout(this._timer);
  2714. },
  2715. emit: function (input) {
  2716. if (this.state !== STATE_RECOGNIZED) {
  2717. return;
  2718. }
  2719. if (input && input.eventType & INPUT_END) {
  2720. this.manager.emit(this.options.event + 'up', input);
  2721. } else {
  2722. this._input.timeStamp = now();
  2723. this.manager.emit(this.options.event, this._input);
  2724. }
  2725. }
  2726. });
  2727. /**
  2728. * Rotate
  2729. * Recognized when two or more pointer are moving in a circular motion.
  2730. * @constructor
  2731. * @extends AttrRecognizer
  2732. */
  2733. function RotateRecognizer() {
  2734. AttrRecognizer.apply(this, arguments);
  2735. }
  2736. inherit(RotateRecognizer, AttrRecognizer, {
  2737. /**
  2738. * @namespace
  2739. * @memberof RotateRecognizer
  2740. */
  2741. defaults: {
  2742. event: 'rotate',
  2743. threshold: 0,
  2744. pointers: 2
  2745. },
  2746. getTouchAction: function () {
  2747. return [TOUCH_ACTION_NONE];
  2748. },
  2749. attrTest: function (input) {
  2750. return this._super.attrTest.call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
  2751. }
  2752. });
  2753. /**
  2754. * Swipe
  2755. * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
  2756. * @constructor
  2757. * @extends AttrRecognizer
  2758. */
  2759. function SwipeRecognizer() {
  2760. AttrRecognizer.apply(this, arguments);
  2761. }
  2762. inherit(SwipeRecognizer, AttrRecognizer, {
  2763. /**
  2764. * @namespace
  2765. * @memberof SwipeRecognizer
  2766. */
  2767. defaults: {
  2768. event: 'swipe',
  2769. threshold: 10,
  2770. velocity: 0.65,
  2771. direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
  2772. pointers: 1
  2773. },
  2774. getTouchAction: function () {
  2775. return PanRecognizer.prototype.getTouchAction.call(this);
  2776. },
  2777. attrTest: function (input) {
  2778. var direction = this.options.direction;
  2779. var velocity;
  2780. if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
  2781. velocity = input.velocity;
  2782. } else if (direction & DIRECTION_HORIZONTAL) {
  2783. velocity = input.velocityX;
  2784. } else if (direction & DIRECTION_VERTICAL) {
  2785. velocity = input.velocityY;
  2786. }
  2787. return this._super.attrTest.call(this, input) && direction & input.direction && input.distance > this.options.threshold && abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
  2788. },
  2789. emit: function (input) {
  2790. var direction = directionStr(input.direction);
  2791. if (direction) {
  2792. this.manager.emit(this.options.event + direction, input);
  2793. }
  2794. this.manager.emit(this.options.event, input);
  2795. }
  2796. });
  2797. /**
  2798. * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
  2799. * between the given interval and position. The delay option can be used to recognize multi-taps without firing
  2800. * a single tap.
  2801. *
  2802. * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
  2803. * multi-taps being recognized.
  2804. * @constructor
  2805. * @extends Recognizer
  2806. */
  2807. function TapRecognizer() {
  2808. Recognizer.apply(this, arguments);
  2809. // previous time and center,
  2810. // used for tap counting
  2811. this.pTime = false;
  2812. this.pCenter = false;
  2813. this._timer = null;
  2814. this._input = null;
  2815. this.count = 0;
  2816. }
  2817. inherit(TapRecognizer, Recognizer, {
  2818. /**
  2819. * @namespace
  2820. * @memberof PinchRecognizer
  2821. */
  2822. defaults: {
  2823. event: 'tap',
  2824. pointers: 1,
  2825. taps: 1,
  2826. interval: 300,
  2827. // max time between the multi-tap taps
  2828. time: 250,
  2829. // max time of the pointer to be down (like finger on the screen)
  2830. threshold: 10,
  2831. // a minimal movement is ok, but keep it low
  2832. posThreshold: 10 // a multi-tap can be a bit off the initial position
  2833. },
  2834. getTouchAction: function () {
  2835. return [TOUCH_ACTION_MANIPULATION];
  2836. },
  2837. process: function (input) {
  2838. var options = this.options;
  2839. var validPointers = input.pointers.length === options.pointers;
  2840. var validMovement = input.distance < options.threshold;
  2841. var validTouchTime = input.deltaTime < options.time;
  2842. this.reset();
  2843. if (input.eventType & INPUT_START && this.count === 0) {
  2844. return this.failTimeout();
  2845. }
  2846. // we only allow little movement
  2847. // and we've reached an end event, so a tap is possible
  2848. if (validMovement && validTouchTime && validPointers) {
  2849. if (input.eventType != INPUT_END) {
  2850. return this.failTimeout();
  2851. }
  2852. var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true;
  2853. var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
  2854. this.pTime = input.timeStamp;
  2855. this.pCenter = input.center;
  2856. if (!validMultiTap || !validInterval) {
  2857. this.count = 1;
  2858. } else {
  2859. this.count += 1;
  2860. }
  2861. this._input = input;
  2862. // if tap count matches we have recognized it,
  2863. // else it has began recognizing...
  2864. var tapCount = this.count % options.taps;
  2865. if (tapCount === 0) {
  2866. // no failing requirements, immediately trigger the tap event
  2867. // or wait as long as the multitap interval to trigger
  2868. if (!this.hasRequireFailures()) {
  2869. return STATE_RECOGNIZED;
  2870. } else {
  2871. this._timer = setTimeoutContext(function () {
  2872. this.state = STATE_RECOGNIZED;
  2873. this.tryEmit();
  2874. }, options.interval, this);
  2875. return STATE_BEGAN;
  2876. }
  2877. }
  2878. }
  2879. return STATE_FAILED;
  2880. },
  2881. failTimeout: function () {
  2882. this._timer = setTimeoutContext(function () {
  2883. this.state = STATE_FAILED;
  2884. }, this.options.interval, this);
  2885. return STATE_FAILED;
  2886. },
  2887. reset: function () {
  2888. clearTimeout(this._timer);
  2889. },
  2890. emit: function () {
  2891. if (this.state == STATE_RECOGNIZED) {
  2892. this._input.tapCount = this.count;
  2893. this.manager.emit(this.options.event, this._input);
  2894. }
  2895. }
  2896. });
  2897. /**
  2898. * Simple way to create an manager with a default set of recognizers.
  2899. * @param {HTMLElement} element
  2900. * @param {Object} [options]
  2901. * @constructor
  2902. */
  2903. function Hammer(element, options) {
  2904. options = options || {};
  2905. options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
  2906. return new Manager(element, options);
  2907. }
  2908. /**
  2909. * @const {string}
  2910. */
  2911. Hammer.VERSION = '2.0.4';
  2912. /**
  2913. * default settings
  2914. * @namespace
  2915. */
  2916. Hammer.defaults = {
  2917. /**
  2918. * set if DOM events are being triggered.
  2919. * But this is slower and unused by simple implementations, so disabled by default.
  2920. * @type {Boolean}
  2921. * @default false
  2922. */
  2923. domEvents: false,
  2924. /**
  2925. * The value for the touchAction property/fallback.
  2926. * When set to `compute` it will magically set the correct value based on the added recognizers.
  2927. * @type {String}
  2928. * @default compute
  2929. */
  2930. touchAction: TOUCH_ACTION_COMPUTE,
  2931. /**
  2932. * @type {Boolean}
  2933. * @default true
  2934. */
  2935. enable: true,
  2936. /**
  2937. * EXPERIMENTAL FEATURE -- can be removed/changed
  2938. * Change the parent input target element.
  2939. * If Null, then it is being set the to main element.
  2940. * @type {Null|EventTarget}
  2941. * @default null
  2942. */
  2943. inputTarget: null,
  2944. /**
  2945. * force an input class
  2946. * @type {Null|Function}
  2947. * @default null
  2948. */
  2949. inputClass: null,
  2950. /**
  2951. * Default recognizer setup when calling `Hammer()`
  2952. * When creating a new Manager these will be skipped.
  2953. * @type {Array}
  2954. */
  2955. preset: [
  2956. // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
  2957. [
  2958. RotateRecognizer,
  2959. { enable: false }
  2960. ],
  2961. [
  2962. PinchRecognizer,
  2963. { enable: false },
  2964. ['rotate']
  2965. ],
  2966. [
  2967. SwipeRecognizer,
  2968. { direction: DIRECTION_HORIZONTAL }
  2969. ],
  2970. [
  2971. PanRecognizer,
  2972. { direction: DIRECTION_HORIZONTAL },
  2973. ['swipe']
  2974. ],
  2975. [TapRecognizer],
  2976. [
  2977. TapRecognizer,
  2978. {
  2979. event: 'doubletap',
  2980. taps: 2
  2981. },
  2982. ['tap']
  2983. ],
  2984. [PressRecognizer]
  2985. ],
  2986. /**
  2987. * Some CSS properties can be used to improve the working of Hammer.
  2988. * Add them to this method and they will be set when creating a new Manager.
  2989. * @namespace
  2990. */
  2991. cssProps: {
  2992. /**
  2993. * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
  2994. * @type {String}
  2995. * @default 'none'
  2996. */
  2997. userSelect: 'none',
  2998. /**
  2999. * Disable the Windows Phone grippers when pressing an element.
  3000. * @type {String}
  3001. * @default 'none'
  3002. */
  3003. touchSelect: 'none',
  3004. /**
  3005. * Disables the default callout shown when you touch and hold a touch target.
  3006. * On iOS, when you touch and hold a touch target such as a link, Safari displays
  3007. * a callout containing information about the link. This property allows you to disable that callout.
  3008. * @type {String}
  3009. * @default 'none'
  3010. */
  3011. touchCallout: 'none',
  3012. /**
  3013. * Specifies whether zooming is enabled. Used by IE10>
  3014. * @type {String}
  3015. * @default 'none'
  3016. */
  3017. contentZooming: 'none',
  3018. /**
  3019. * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
  3020. * @type {String}
  3021. * @default 'none'
  3022. */
  3023. userDrag: 'none',
  3024. /**
  3025. * Overrides the highlight color shown when the user taps a link or a JavaScript
  3026. * clickable element in iOS. This property obeys the alpha value, if specified.
  3027. * @type {String}
  3028. * @default 'rgba(0,0,0,0)'
  3029. */
  3030. tapHighlightColor: 'rgba(0,0,0,0)'
  3031. }
  3032. };
  3033. var STOP = 1;
  3034. var FORCED_STOP = 2;
  3035. /**
  3036. * Manager
  3037. * @param {HTMLElement} element
  3038. * @param {Object} [options]
  3039. * @constructor
  3040. */
  3041. function Manager(element, options) {
  3042. options = options || {};
  3043. this.options = merge(options, Hammer.defaults);
  3044. this.options.inputTarget = this.options.inputTarget || element;
  3045. this.handlers = {};
  3046. this.session = {};
  3047. this.recognizers = [];
  3048. this.element = element;
  3049. this.input = createInputInstance(this);
  3050. this.touchAction = new TouchAction(this, this.options.touchAction);
  3051. toggleCssProps(this, true);
  3052. each(options.recognizers, function (item) {
  3053. var recognizer = this.add(new item[0](item[1]));
  3054. item[2] && recognizer.recognizeWith(item[2]);
  3055. item[3] && recognizer.requireFailure(item[3]);
  3056. }, this);
  3057. }
  3058. Manager.prototype = {
  3059. /**
  3060. * set options
  3061. * @param {Object} options
  3062. * @returns {Manager}
  3063. */
  3064. set: function (options) {
  3065. extend(this.options, options);
  3066. // Options that need a little more setup
  3067. if (options.touchAction) {
  3068. this.touchAction.update();
  3069. }
  3070. if (options.inputTarget) {
  3071. // Clean up existing event listeners and reinitialize
  3072. this.input.destroy();
  3073. this.input.target = options.inputTarget;
  3074. this.input.init();
  3075. }
  3076. return this;
  3077. },
  3078. /**
  3079. * stop recognizing for this session.
  3080. * This session will be discarded, when a new [input]start event is fired.
  3081. * When forced, the recognizer cycle is stopped immediately.
  3082. * @param {Boolean} [force]
  3083. */
  3084. stop: function (force) {
  3085. this.session.stopped = force ? FORCED_STOP : STOP;
  3086. },
  3087. /**
  3088. * run the recognizers!
  3089. * called by the inputHandler function on every movement of the pointers (touches)
  3090. * it walks through all the recognizers and tries to detect the gesture that is being made
  3091. * @param {Object} inputData
  3092. */
  3093. recognize: function (inputData) {
  3094. var session = this.session;
  3095. if (session.stopped) {
  3096. return;
  3097. }
  3098. // run the touch-action polyfill
  3099. this.touchAction.preventDefaults(inputData);
  3100. var recognizer;
  3101. var recognizers = this.recognizers;
  3102. // this holds the recognizer that is being recognized.
  3103. // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
  3104. // if no recognizer is detecting a thing, it is set to `null`
  3105. var curRecognizer = session.curRecognizer;
  3106. // reset when the last recognizer is recognized
  3107. // or when we're in a new session
  3108. if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) {
  3109. curRecognizer = session.curRecognizer = null;
  3110. }
  3111. var i = 0;
  3112. while (i < recognizers.length) {
  3113. recognizer = recognizers[i];
  3114. // find out if we are allowed try to recognize the input for this one.
  3115. // 1. allow if the session is NOT forced stopped (see the .stop() method)
  3116. // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
  3117. // that is being recognized.
  3118. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
  3119. // this can be setup with the `recognizeWith()` method on the recognizer.
  3120. if (session.stopped !== FORCED_STOP && // 1
  3121. (!curRecognizer || recognizer == curRecognizer || // 2
  3122. recognizer.canRecognizeWith(curRecognizer))) {
  3123. // 3
  3124. recognizer.recognize(inputData);
  3125. } else {
  3126. recognizer.reset();
  3127. }
  3128. // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
  3129. // current active recognizer. but only if we don't already have an active recognizer
  3130. if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
  3131. curRecognizer = session.curRecognizer = recognizer;
  3132. }
  3133. i++;
  3134. }
  3135. },
  3136. /**
  3137. * get a recognizer by its event name.
  3138. * @param {Recognizer|String} recognizer
  3139. * @returns {Recognizer|Null}
  3140. */
  3141. get: function (recognizer) {
  3142. if (recognizer instanceof Recognizer) {
  3143. return recognizer;
  3144. }
  3145. var recognizers = this.recognizers;
  3146. for (var i = 0; i < recognizers.length; i++) {
  3147. if (recognizers[i].options.event == recognizer) {
  3148. return recognizers[i];
  3149. }
  3150. }
  3151. return null;
  3152. },
  3153. /**
  3154. * add a recognizer to the manager
  3155. * existing recognizers with the same event name will be removed
  3156. * @param {Recognizer} recognizer
  3157. * @returns {Recognizer|Manager}
  3158. */
  3159. add: function (recognizer) {
  3160. if (invokeArrayArg(recognizer, 'add', this)) {
  3161. return this;
  3162. }
  3163. // remove existing
  3164. var existing = this.get(recognizer.options.event);
  3165. if (existing) {
  3166. this.remove(existing);
  3167. }
  3168. this.recognizers.push(recognizer);
  3169. recognizer.manager = this;
  3170. this.touchAction.update();
  3171. return recognizer;
  3172. },
  3173. /**
  3174. * remove a recognizer by name or instance
  3175. * @param {Recognizer|String} recognizer
  3176. * @returns {Manager}
  3177. */
  3178. remove: function (recognizer) {
  3179. if (invokeArrayArg(recognizer, 'remove', this)) {
  3180. return this;
  3181. }
  3182. var recognizers = this.recognizers;
  3183. recognizer = this.get(recognizer);
  3184. recognizers.splice(inArray(recognizers, recognizer), 1);
  3185. this.touchAction.update();
  3186. return this;
  3187. },
  3188. /**
  3189. * bind event
  3190. * @param {String} events
  3191. * @param {Function} handler
  3192. * @returns {EventEmitter} this
  3193. */
  3194. on: function (events, handler) {
  3195. var handlers = this.handlers;
  3196. each(splitStr(events), function (event) {
  3197. handlers[event] = handlers[event] || [];
  3198. handlers[event].push(handler);
  3199. });
  3200. return this;
  3201. },
  3202. /**
  3203. * unbind event, leave emit blank to remove all handlers
  3204. * @param {String} events
  3205. * @param {Function} [handler]
  3206. * @returns {EventEmitter} this
  3207. */
  3208. off: function (events, handler) {
  3209. var handlers = this.handlers;
  3210. each(splitStr(events), function (event) {
  3211. if (!handler) {
  3212. delete handlers[event];
  3213. } else {
  3214. handlers[event].splice(inArray(handlers[event], handler), 1);
  3215. }
  3216. });
  3217. return this;
  3218. },
  3219. /**
  3220. * emit event to the listeners
  3221. * @param {String} event
  3222. * @param {Object} data
  3223. */
  3224. emit: function (event, data) {
  3225. // we also want to trigger dom events
  3226. if (this.options.domEvents) {
  3227. triggerDomEvent(event, data);
  3228. }
  3229. // no handlers, so skip it all
  3230. var handlers = this.handlers[event] && this.handlers[event].slice();
  3231. if (!handlers || !handlers.length) {
  3232. return;
  3233. }
  3234. data.type = event;
  3235. data.preventDefault = function () {
  3236. data.srcEvent.preventDefault();
  3237. };
  3238. var i = 0;
  3239. while (i < handlers.length) {
  3240. handlers[i](data);
  3241. i++;
  3242. }
  3243. },
  3244. /**
  3245. * destroy the manager and unbinds all events
  3246. * it doesn't unbind dom events, that is the user own responsibility
  3247. */
  3248. destroy: function () {
  3249. this.element && toggleCssProps(this, false);
  3250. this.handlers = {};
  3251. this.session = {};
  3252. this.input.destroy();
  3253. this.element = null;
  3254. }
  3255. };
  3256. /**
  3257. * add/remove the css properties as defined in manager.options.cssProps
  3258. * @param {Manager} manager
  3259. * @param {Boolean} add
  3260. */
  3261. function toggleCssProps(manager, add) {
  3262. var element = manager.element;
  3263. each(manager.options.cssProps, function (value, name) {
  3264. element.style[prefixed(element.style, name)] = add ? value : '';
  3265. });
  3266. }
  3267. /**
  3268. * trigger dom event
  3269. * @param {String} event
  3270. * @param {Object} data
  3271. */
  3272. function triggerDomEvent(event, data) {
  3273. var gestureEvent = document.createEvent('Event');
  3274. gestureEvent.initEvent(event, true, true);
  3275. gestureEvent.gesture = data;
  3276. data.target.dispatchEvent(gestureEvent);
  3277. }
  3278. extend(Hammer, {
  3279. INPUT_START: INPUT_START,
  3280. INPUT_MOVE: INPUT_MOVE,
  3281. INPUT_END: INPUT_END,
  3282. INPUT_CANCEL: INPUT_CANCEL,
  3283. STATE_POSSIBLE: STATE_POSSIBLE,
  3284. STATE_BEGAN: STATE_BEGAN,
  3285. STATE_CHANGED: STATE_CHANGED,
  3286. STATE_ENDED: STATE_ENDED,
  3287. STATE_RECOGNIZED: STATE_RECOGNIZED,
  3288. STATE_CANCELLED: STATE_CANCELLED,
  3289. STATE_FAILED: STATE_FAILED,
  3290. DIRECTION_NONE: DIRECTION_NONE,
  3291. DIRECTION_LEFT: DIRECTION_LEFT,
  3292. DIRECTION_RIGHT: DIRECTION_RIGHT,
  3293. DIRECTION_UP: DIRECTION_UP,
  3294. DIRECTION_DOWN: DIRECTION_DOWN,
  3295. DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
  3296. DIRECTION_VERTICAL: DIRECTION_VERTICAL,
  3297. DIRECTION_ALL: DIRECTION_ALL,
  3298. Manager: Manager,
  3299. Input: Input,
  3300. TouchAction: TouchAction,
  3301. TouchInput: TouchInput,
  3302. MouseInput: MouseInput,
  3303. PointerEventInput: PointerEventInput,
  3304. TouchMouseInput: TouchMouseInput,
  3305. SingleTouchInput: SingleTouchInput,
  3306. Recognizer: Recognizer,
  3307. AttrRecognizer: AttrRecognizer,
  3308. Tap: TapRecognizer,
  3309. Pan: PanRecognizer,
  3310. Swipe: SwipeRecognizer,
  3311. Pinch: PinchRecognizer,
  3312. Rotate: RotateRecognizer,
  3313. Press: PressRecognizer,
  3314. on: addEventListeners,
  3315. off: removeEventListeners,
  3316. each: each,
  3317. merge: merge,
  3318. extend: extend,
  3319. inherit: inherit,
  3320. bindFn: bindFn,
  3321. prefixed: prefixed
  3322. });
  3323. if (typeof module == 'object' && module.exports) {
  3324. exports = Hammer;
  3325. } /** ignored by jsdoc **/ else {
  3326. return Hammer;
  3327. }
  3328. return exports;
  3329. }(hammer);
  3330. boundry = function (exports) {
  3331. var Util = util;
  3332. function Boundry(cfg) {
  3333. this.cfg = Util.mix({
  3334. width: 0,
  3335. height: 0
  3336. }, cfg);
  3337. this.init();
  3338. }
  3339. Util.mix(Boundry.prototype, {
  3340. init: function () {
  3341. var self = this;
  3342. self._xtop = 0;
  3343. self._xright = 0;
  3344. self._xleft = 0;
  3345. self._xbottom = 0;
  3346. self.refresh({
  3347. width: self.cfg.width,
  3348. height: self.cfg.height
  3349. });
  3350. },
  3351. reset: function () {
  3352. this.resetTop();
  3353. this.resetLeft();
  3354. this.resetBottom();
  3355. this.resetRight();
  3356. return this;
  3357. },
  3358. resetTop: function () {
  3359. this._xtop = 0;
  3360. this.refresh();
  3361. return this;
  3362. },
  3363. resetLeft: function () {
  3364. this._xleft = 0;
  3365. this.refresh();
  3366. return this;
  3367. },
  3368. resetBottom: function () {
  3369. this._xbottom = 0;
  3370. this.refresh();
  3371. return this;
  3372. },
  3373. resetRight: function () {
  3374. this._xright = 0;
  3375. this.refresh();
  3376. return this;
  3377. },
  3378. expandTop: function (top) {
  3379. this._xtop = top;
  3380. this.refresh();
  3381. return this;
  3382. },
  3383. expandLeft: function (left) {
  3384. this._xleft = left;
  3385. this.refresh();
  3386. return this;
  3387. },
  3388. expandRight: function (right) {
  3389. this._xright = right;
  3390. this.refresh();
  3391. return this;
  3392. },
  3393. expandBottom: function (bottom) {
  3394. this._xbottom = bottom;
  3395. this.refresh();
  3396. return this;
  3397. },
  3398. refresh: function (cfg) {
  3399. Util.mix(this.cfg, cfg);
  3400. this.top = this._xtop;
  3401. this.left = this._xleft;
  3402. this.bottom = (cfg && cfg.height || this.cfg.height || 0) - this._xbottom;
  3403. this.right = (cfg && cfg.width || this.cfg.width || 0) - this._xright;
  3404. this.width = this.right - this.left > 0 ? this.right - this.left : 0;
  3405. this.height = this.bottom - this.top > 0 ? this.bottom - this.top : 0;
  3406. return this;
  3407. }
  3408. });
  3409. if (typeof module == 'object' && module.exports) {
  3410. exports = Boundry;
  3411. } /** ignored by jsdoc **/ else {
  3412. return Boundry;
  3413. }
  3414. return exports;
  3415. }(boundry);
  3416. components_sticky = function (exports) {
  3417. var Util = util;
  3418. var Base = base;
  3419. //transform
  3420. var transform = Util.prefixStyle('transform');
  3421. // default render function for position:sticky elements
  3422. var defaultStickyRenderFunc = function (e) {
  3423. var stickyElement = e.stickyElement;
  3424. var curStickyElement = e.curStickyElement;
  3425. var xscroll = e.xscroll;
  3426. var _ = e._;
  3427. var infinite = xscroll.getPlugin('infinite');
  3428. if (infinite) {
  3429. infinite.userConfig.renderHook.call(self, stickyElement, curStickyElement);
  3430. stickyElement.setAttribute('xs-guid', curStickyElement.guid);
  3431. Util.addClass(stickyElement, curStickyElement.className);
  3432. for (var attrName in curStickyElement.style) {
  3433. if (attrName != 'display' && attrName != 'position') {
  3434. //copy styles
  3435. stickyElement.style[attrName] = attrName == _.height ? curStickyElement.style[attrName] + 'px' : curStickyElement.style[attrName];
  3436. }
  3437. }
  3438. } else {
  3439. var style = curStickyElement.getAttribute('style');
  3440. stickyElement.innerHTML = curStickyElement.innerHTML;
  3441. stickyElement.className = curStickyElement.className;
  3442. style && stickyElement.setAttribute('style', style);
  3443. }
  3444. };
  3445. var Sticky = function (cfg) {
  3446. Sticky.superclass.constructor.call(this, cfg);
  3447. this.userConfig = Util.mix({
  3448. stickyRenderTo: undefined,
  3449. forceSticky: true,
  3450. prefix: 'xs-sticky-container',
  3451. stickyRenderFunc: defaultStickyRenderFunc,
  3452. zoomType: 'y'
  3453. }, cfg);
  3454. this.init();
  3455. };
  3456. Util.extend(Sticky, Base, {
  3457. init: function () {
  3458. var self = this, userConfig = self.userConfig, xscroll = self.xscroll = userConfig.xscroll;
  3459. var isY = self.isY = !!(userConfig.zoomType == 'y');
  3460. self._ = {
  3461. top: self.isY ? 'top' : 'left',
  3462. left: self.isY ? 'left' : 'bottom',
  3463. right: self.isY ? 'right' : 'top',
  3464. height: self.isY ? 'height' : 'width',
  3465. width: self.isY ? 'width' : 'height'
  3466. };
  3467. self.stickyRenderTo = Util.getNode(userConfig.stickyRenderTo);
  3468. self._handlers = [];
  3469. return self;
  3470. },
  3471. getStickiesPos: function () {
  3472. var self = this;
  3473. var xscroll = self.xscroll;
  3474. var isInfinite = self.isInfinite;
  3475. var isY = self.isY;
  3476. var _ = self._;
  3477. var stickiesPos = [];
  3478. var getPos = function (sticky) {
  3479. var pos = {};
  3480. if (isInfinite) {
  3481. pos[_.top] = isY ? sticky._top : sticky._left;
  3482. pos[_.height] = isY ? sticky._height : sticky._width;
  3483. } else {
  3484. pos[_.top] = self.isY ? Util.getOffsetTop(sticky) : Util.getOffsetLeft(sticky);
  3485. pos[_.height] = self.isY ? sticky.offsetHeight : sticky.offsetWidth;
  3486. }
  3487. return pos;
  3488. };
  3489. for (var i = 0; i < self.stickiesNum; i++) {
  3490. var pos = getPos(self.stickyElements[i]);
  3491. self._handlers[i] = self._handlers[i] || self.createStickyEl();
  3492. pos.el = self._handlers[i];
  3493. pos.isRender = false;
  3494. stickiesPos.push(pos);
  3495. }
  3496. return stickiesPos;
  3497. },
  3498. getStickyElements: function () {
  3499. var self = this;
  3500. var xscroll = self.xscroll;
  3501. var userConfig = self.userConfig;
  3502. var isInfinite = self.isInfinite;
  3503. var infinite = xscroll.getPlugin('infinite');
  3504. if (infinite) {
  3505. var stickyElements = [], serializedData = infinite.__serializedData;
  3506. for (var i in serializedData) {
  3507. var rowData = serializedData[i];
  3508. if (rowData && rowData.style && 'sticky' == rowData.style.position) {
  3509. stickyElements.push(rowData);
  3510. }
  3511. }
  3512. return stickyElements;
  3513. } else {
  3514. return Util.getNodes(xscroll.userConfig.stickyElements, xscroll.content);
  3515. }
  3516. },
  3517. render: function (force) {
  3518. var self = this;
  3519. var userConfig = self.userConfig;
  3520. var xscroll = self.xscroll;
  3521. self.isInfinite = !!xscroll.getPlugin('infinite');
  3522. var _ = self._;
  3523. self.stickyElements = self.getStickyElements();
  3524. self.stickiesNum = self.stickyElements && self.stickyElements.length;
  3525. if (!self.stickiesNum)
  3526. return;
  3527. if (!self.stickyRenderTo) {
  3528. self.stickyRenderTo = document.createElement('div');
  3529. xscroll.renderTo.appendChild(self.stickyRenderTo);
  3530. }
  3531. self.stickiesPos = self.getStickiesPos();
  3532. var stickyRenderTo = self.stickyRenderTo;
  3533. stickyRenderTo.style[_.top] = 0;
  3534. stickyRenderTo.style[_.left] = 0;
  3535. stickyRenderTo.style[_.right] = 0;
  3536. stickyRenderTo.style.position = xscroll.userConfig.useOriginScroll ? 'fixed' : 'absolute';
  3537. Util.addClass(self.stickyRenderTo, userConfig.prefix);
  3538. self.stickyHandler(force);
  3539. self._bindEvt();
  3540. },
  3541. createStickyEl: function () {
  3542. var self = this;
  3543. var el = document.createElement('div');
  3544. el.style.display = 'none';
  3545. Util.addClass(el, 'xs-sticky-handler');
  3546. self.stickyRenderTo.appendChild(el);
  3547. return el;
  3548. },
  3549. _bindEvt: function () {
  3550. var self = this, xscroll = self.xscroll;
  3551. xscroll.on('scroll', self.stickyHandler, self);
  3552. },
  3553. stickyHandler: function (force) {
  3554. var self = this;
  3555. var xscroll = self.xscroll;
  3556. var userConfig = self.userConfig;
  3557. var scrollTop = self.isY ? xscroll.getScrollTop() : xscroll.getScrollLeft();
  3558. var stickiesPos = self.stickiesPos;
  3559. var _ = self._;
  3560. var indexes = [];
  3561. for (var i = 0, l = stickiesPos.length; i < l; i++) {
  3562. var top = stickiesPos[i][_.top];
  3563. if (scrollTop > top) {
  3564. indexes.push(i);
  3565. }
  3566. }
  3567. if (!indexes.length) {
  3568. if (self.stickyElement) {
  3569. self.stickyElement.style.display = 'none';
  3570. }
  3571. self.curStickyIndex = undefined;
  3572. return;
  3573. }
  3574. var curStickyIndex = Math.max.apply(null, indexes);
  3575. if (self.curStickyIndex != curStickyIndex || force) {
  3576. var prevStickyIndex = self.curStickyIndex;
  3577. self.curStickyIndex = curStickyIndex;
  3578. self.curStickyElement = self.stickyElements[curStickyIndex];
  3579. self.curStickyPos = stickiesPos[curStickyIndex];
  3580. self.stickyElement = self.curStickyPos.el;
  3581. for (var i = 0, l = stickiesPos.length; i < l; i++) {
  3582. stickiesPos[i].el.style.display = 'none';
  3583. }
  3584. var eventsObj = {
  3585. stickyElement: self.stickyElement,
  3586. curStickyIndex: self.curStickyIndex,
  3587. prevStickyIndex: prevStickyIndex,
  3588. curStickyPos: self.curStickyPos,
  3589. isRender: self.curStickyPos.isRender
  3590. };
  3591. xscroll.trigger('beforestickychange', eventsObj);
  3592. self._stickyRenderFunc(self);
  3593. xscroll.trigger('stickychange', eventsObj);
  3594. }
  3595. var trans = 0;
  3596. if (self.stickiesPos[self.curStickyIndex + 1]) {
  3597. var cur = self.stickiesPos[self.curStickyIndex];
  3598. var next = self.stickiesPos[self.curStickyIndex + 1];
  3599. if (scrollTop + cur[_.height] > next[_.top] && scrollTop + cur[_.height] < next[_.top] + cur[_.height]) {
  3600. trans = cur[_.height] + scrollTop - next[_.top];
  3601. } else {
  3602. trans = 0;
  3603. }
  3604. }
  3605. self.stickyElement.style[transform] = self.isY ? 'translateY(-' + trans + 'px) translateZ(0)' : 'translateX(-' + trans + 'px) translateZ(0)';
  3606. },
  3607. _stickyRenderFunc: function (e) {
  3608. var self = this;
  3609. var _ = self._;
  3610. var stickyRenderFunc = self.userConfig.stickyRenderFunc;
  3611. var el = self.curStickyPos.el;
  3612. if (!self.curStickyPos.isRender) {
  3613. el.style[_.left] = 0;
  3614. el.style[_.right] = 0;
  3615. stickyRenderFunc && stickyRenderFunc.call(self, e);
  3616. }
  3617. el.style.display = 'block';
  3618. self.curStickyPos.isRender = true;
  3619. },
  3620. destroy: function () {
  3621. var self = this;
  3622. self.stickyElements = undefined;
  3623. self.stickiesNum = undefined;
  3624. self.stickiesPos = undefined;
  3625. Util.remove(self.stickyElement);
  3626. self.stickyElement = undefined;
  3627. }
  3628. });
  3629. if (typeof module == 'object' && module.exports) {
  3630. exports = Sticky;
  3631. } /** ignored by jsdoc **/ else {
  3632. return Sticky;
  3633. }
  3634. return exports;
  3635. }(components_sticky);
  3636. components_fixed = function (exports) {
  3637. var Util = util;
  3638. var Base = base;
  3639. var transform = Util.prefixStyle('transform');
  3640. var Fixed = function (cfg) {
  3641. Fixed.superclass.constructor.call(this, cfg);
  3642. this.userConfig = Util.mix({
  3643. fixedRenderTo: undefined,
  3644. fixedElements: '.xs-fixed',
  3645. prefix: 'xs-fixed-container',
  3646. zoomType: 'y'
  3647. }, cfg);
  3648. this.init();
  3649. };
  3650. Util.extend(Fixed, Base, {
  3651. fixedElements: [],
  3652. init: function () {
  3653. var self = this, userConfig = self.userConfig, xscroll = self.xscroll = userConfig.xscroll, xscrollConfig = self.xscrollConfig = xscroll.userConfig;
  3654. self.isY = !!(userConfig.zoomType == 'y');
  3655. self._ = self.isY ? {
  3656. top: 'top',
  3657. height: 'height',
  3658. width: 'width',
  3659. offsetTop: 'offsetTop'
  3660. } : {
  3661. top: 'left',
  3662. height: 'width',
  3663. width: 'height',
  3664. offsetTop: 'offsetLeft'
  3665. };
  3666. self.fixedRenderTo = Util.getNode(userConfig.fixedRenderTo);
  3667. return self;
  3668. },
  3669. render: function () {
  3670. var self = this;
  3671. var xscroll = self.xscroll;
  3672. self.infinite = xscroll.getPlugin('infinite');
  3673. if (!self.fixedRenderTo) {
  3674. self.fixedRenderTo = document.createElement('div');
  3675. xscroll.renderTo.appendChild(self.fixedRenderTo);
  3676. }
  3677. Util.addClass(self.fixedRenderTo, self.userConfig.prefix);
  3678. var originalFixedElements = self.originalFixedElements = self.getFixedElements();
  3679. for (var i = 0, l = originalFixedElements.length; i < l; i++) {
  3680. self.renderFixedElement(originalFixedElements[i], i, self.fixedRenderTo);
  3681. }
  3682. return self;
  3683. },
  3684. getFixedElements: function () {
  3685. var self = this;
  3686. var infinite = self.infinite;
  3687. var userConfig = self.userConfig;
  3688. if (infinite) {
  3689. var els = [];
  3690. for (var i in infinite.__serializedData) {
  3691. var data = infinite.__serializedData[i];
  3692. if (data && data.style && data.style.position == 'fixed') {
  3693. els.push(data);
  3694. }
  3695. }
  3696. return els;
  3697. } else {
  3698. return Util.getNodes(userConfig.fixedElements, self.xscroll.content);
  3699. }
  3700. },
  3701. renderFixedElement: function (el, fixedIndex, fixedRenderTo) {
  3702. var self = this;
  3703. var isRender = true;
  3704. var _ = self._;
  3705. var xscroll = self.xscroll;
  3706. var userConfig = self.userConfig;
  3707. var xscrollConfig = self.xscrollConfig;
  3708. var useOriginScroll = xscrollConfig.useOriginScroll;
  3709. var infinite = self.infinite;
  3710. var fixedElement = self.fixedElements[fixedIndex];
  3711. if (!self.fixedElements[fixedIndex]) {
  3712. isRender = false;
  3713. if (useOriginScroll && !infinite) {
  3714. //use original position:fixed stylesheet
  3715. el.style.position = 'fixed';
  3716. el.style.display = 'block';
  3717. } else {
  3718. //deep clone fixed nodes and hide original nodes
  3719. fixedElement = document.createElement('div');
  3720. if (infinite) {
  3721. fixedElement.setAttribute('style', Util.stringifyStyle(Util.mix(el.style, {
  3722. display: 'block',
  3723. width: '100%'
  3724. })));
  3725. fixedElement.style[_.top] = (el.style[_.top] >= 0 ? el.style[_.top] : el._top) + 'px';
  3726. if (el.style[_.height]) {
  3727. fixedElement.style[_.height] = el.style[_.height] + 'px';
  3728. }
  3729. infinite.userConfig.renderHook.call(self, fixedElement, el);
  3730. } else {
  3731. fixedElement.style.display = 'block';
  3732. fixedElement.style.position = 'absolute';
  3733. fixedElement.style[_.width] = '100%';
  3734. fixedElement.innerHTML = el.innerHTML;
  3735. fixedElement.className = el.className;
  3736. fixedElement.setAttribute('style', el.getAttribute('style'));
  3737. fixedElement.style[_.top] = el[_.offsetTop] + 'px';
  3738. el.style.display = 'none';
  3739. }
  3740. fixedRenderTo.appendChild(fixedElement);
  3741. self.fixedElements.push(fixedElement);
  3742. }
  3743. }
  3744. xscroll.trigger('fixedchange', {
  3745. fixedIndex: fixedIndex,
  3746. fixedElement: useOriginScroll ? el : fixedElement,
  3747. originalFixedElement: el,
  3748. isRender: isRender
  3749. });
  3750. },
  3751. destroy: function () {
  3752. var self = this;
  3753. self.fixedElements = undefined;
  3754. }
  3755. });
  3756. if (typeof module == 'object' && module.exports) {
  3757. exports = Fixed;
  3758. } /** ignored by jsdoc **/ else {
  3759. return Fixed;
  3760. }
  3761. return exports;
  3762. }(components_fixed);
  3763. core = function (exports) {
  3764. var Util = util, Base = base, Animate = animate, Boundry = boundry, Hammer = hammer, Sticky = components_sticky, Fixed = components_fixed;
  3765. // boundry checked bounce effect
  3766. var BOUNDRY_CHECK_DURATION = 500;
  3767. var BOUNDRY_CHECK_EASING = 'ease';
  3768. var BOUNDRY_CHECK_ACCELERATION = 0.1;
  3769. /**
  3770. * @constructor
  3771. * @param {object} cfg config for scroll
  3772. * @param {number} cfg.SCROLL_ACCELERATION acceleration for scroll, min value make the scrolling smoothly
  3773. * @param {number} cfg.BOUNDRY_CHECK_DURATION duration for boundry bounce
  3774. * @param {number} cfg.BOUNDRY_CHECK_EASING easing for boundry bounce
  3775. * @param {number} cfg.BOUNDRY_CHECK_ACCELERATION acceleration for boundry bounce
  3776. * @param {boolean} cfg.lockX just like overflow-x:hidden
  3777. * @param {boolean} cfg.lockY just like overflow-y:hidden
  3778. * @param {boolean} cfg.scrollbarX config if the scrollbar-x is visible
  3779. * @param {boolean} cfg.scrollbarY config if the scrollbar-y is visible
  3780. * @param {boolean} cfg.useTransition config if use css3 transition or raf for scroll animation
  3781. * @param {boolean} cfg.useOriginScroll config if use simulate or origin scroll
  3782. * @param {boolean} cfg.bounce config if use has the bounce effect when scrolling outside of the boundry
  3783. * @param {boolean} cfg.boundryCheck config if scrolling inside of the boundry
  3784. * @param {boolean} cfg.preventDefault prevent touchstart
  3785. * @param {boolean} cfg.preventTouchMove prevent touchmove
  3786. * @param {string|HTMLElement} cfg.container config for scroller's container which default value is ".xs-container"
  3787. * @param {string|HTMLElement} cfg.content config for scroller's content which default value is ".xs-content"
  3788. * @param {object} cfg.indicatorInsets config scrollbars position {top: number, left: number, bottom: number, right: number}
  3789. * @param {string} cfg.stickyElements config for sticky-positioned elements
  3790. * @param {string} cfg.fixedElements config for fixed-positioned elements
  3791. * @param {string} cfg.touchAction config for touchAction of the scroller
  3792. * @extends XScroll
  3793. * @example
  3794. * var xscroll = new XScroll({
  3795. * renderTo:"#scroll",
  3796. * lockX:false,
  3797. * scrollbarX:true
  3798. * });
  3799. * xscroll.render();
  3800. */
  3801. function XScroll(cfg) {
  3802. XScroll.superclass.constructor.call(this);
  3803. this.userConfig = cfg;
  3804. this.init();
  3805. }
  3806. Util.extend(XScroll, Base, {
  3807. /**
  3808. * version
  3809. * @memberof XScroll
  3810. * @type {string}
  3811. */
  3812. version: '3.0.13',
  3813. /**
  3814. * init scroll
  3815. * @memberof XScroll
  3816. * @return {XScroll}
  3817. */
  3818. init: function () {
  3819. var self = this;
  3820. var defaultCfg = {
  3821. preventDefault: true,
  3822. bounce: true,
  3823. boundryCheck: true,
  3824. useTransition: true,
  3825. gpuAcceleration: true,
  3826. BOUNDRY_CHECK_EASING: BOUNDRY_CHECK_EASING,
  3827. BOUNDRY_CHECK_DURATION: BOUNDRY_CHECK_DURATION,
  3828. BOUNDRY_CHECK_ACCELERATION: BOUNDRY_CHECK_ACCELERATION,
  3829. useOriginScroll: false,
  3830. zoomType: 'y',
  3831. indicatorInsets: {
  3832. top: 3,
  3833. bottom: 3,
  3834. left: 3,
  3835. right: 3,
  3836. width: 3,
  3837. spacing: 5
  3838. },
  3839. container: '.xs-container',
  3840. content: '.xs-content',
  3841. stickyElements: '.xs-sticky',
  3842. fixedElements: '.xs-fixed',
  3843. touchAction: 'auto'
  3844. };
  3845. //generate guid
  3846. self.guid = Util.guid();
  3847. self.renderTo = Util.getNode(self.userConfig.renderTo);
  3848. //timer for animtion
  3849. self.__timers = {};
  3850. //config attributes on element
  3851. var elCfg = JSON.parse(self.renderTo.getAttribute('xs-cfg'));
  3852. var userConfig = self.userConfig = Util.mix(Util.mix(defaultCfg, elCfg), self.userConfig);
  3853. self.container = Util.getNode(userConfig.container, self.renderTo);
  3854. self.content = Util.getNode(userConfig.content, self.renderTo);
  3855. self.boundry = new Boundry();
  3856. self.boundry.refresh();
  3857. return self;
  3858. },
  3859. /**
  3860. * destroy scroll
  3861. * @memberof XScroll
  3862. * @return {XScroll}
  3863. */
  3864. destroy: function () {
  3865. var self = this;
  3866. self.mc && self.mc.destroy();
  3867. self.sticky && self.sticky.destroy();
  3868. self.fixed && self.fixed.destroy();
  3869. },
  3870. _initContainer: function () {
  3871. },
  3872. /**
  3873. * @memberof XScroll
  3874. * @return {XScroll}
  3875. */
  3876. enableGPUAcceleration: function () {
  3877. this.userConfig.gpuAcceleration = true;
  3878. return this;
  3879. },
  3880. /**
  3881. * @memberof XScroll
  3882. * @return {XScroll}
  3883. */
  3884. disableGPUAcceleration: function () {
  3885. this.userConfig.gpuAcceleration = false;
  3886. return this;
  3887. },
  3888. /**
  3889. * get scroll offset
  3890. * @memberof XScroll
  3891. * @return {Object} {scrollTop:scrollTop,scrollLeft:scrollLeft}
  3892. */
  3893. getScrollPos: function () {
  3894. var self = this;
  3895. return {
  3896. scrollLeft: self.getScrollLeft(),
  3897. scrollTop: self.getScrollTop()
  3898. };
  3899. },
  3900. /**
  3901. * get scroll top value
  3902. * @memberof XScroll
  3903. * @return {number} scrollTop
  3904. */
  3905. getScrollTop: function () {
  3906. },
  3907. /**
  3908. * get scroll left value
  3909. * @memberof XScroll
  3910. * @return {number} scrollLeft
  3911. */
  3912. getScrollLeft: function () {
  3913. },
  3914. /**
  3915. * scroll absolute to the destination
  3916. * @memberof XScroll
  3917. * @param scrollLeft {number} scrollLeft
  3918. * @param scrollTop {number} scrollTop
  3919. * @param duration {number} duration for animte
  3920. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3921. **/
  3922. scrollTo: function (scrollLeft, scrollTop, duration, easing, callback) {
  3923. var self = this;
  3924. var scrollLeft = undefined === scrollLeft || isNaN(scrollLeft) ? -self.getScrollLeft() : scrollLeft;
  3925. var scrollTop = undefined === scrollTop || isNaN(scrollTop) ? -self.getScrollTop() : scrollTop;
  3926. self.scrollLeft(scrollLeft, duration, easing, callback);
  3927. self.scrollTop(scrollTop, duration, easing, callback);
  3928. },
  3929. /**
  3930. * scroll relative to the destination
  3931. * @memberof XScroll
  3932. * @param scrollLeft {number} scrollLeft
  3933. * @param scrollTop {number} scrollTop
  3934. * @param duration {number} duration for animte
  3935. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3936. **/
  3937. scrollBy: function (scrollLeft, scrollTop, duration, easing, callback) {
  3938. this.scrollByX(scrollLeft, duration, easing, callback);
  3939. this.scrollByY(scrollTop, duration, easing, callback);
  3940. },
  3941. /**
  3942. * horizontal scroll relative to the destination
  3943. * @memberof XScroll
  3944. * @param scrollLeft {number} scrollLeft
  3945. * @param duration {number} duration for animte
  3946. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3947. **/
  3948. scrollLeftBy: function (scrollLeft, duration, easing, callback) {
  3949. this.scrollLeft(Number(scrollLeft) + Number(this.getScrollLeft()), duration, easing, callback);
  3950. },
  3951. /**
  3952. * vertical scroll relative to the destination
  3953. * @memberof XScroll
  3954. * @param scrollTop {number} scrollTop
  3955. * @param duration {number} duration for animte
  3956. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3957. **/
  3958. scrollTopBy: function (scrollTop, duration, easing, callback) {
  3959. this.scrollTop(Number(scrollTop) + Number(this.getScrollTop()), duration, easing, callback);
  3960. },
  3961. /**
  3962. * horizontal scroll absolute to the destination
  3963. * @memberof XScroll
  3964. * @param scrollLeft {number} scrollLeft
  3965. * @param duration {number} duration for animte
  3966. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3967. **/
  3968. scrollLeft: function (scrollLeft, duration, easing, callback) {
  3969. },
  3970. /**
  3971. * vertical scroll absolute to the destination
  3972. * @memberof XScroll
  3973. * @param scrollTop {number} scrollTop
  3974. * @param duration {number} duration for animte
  3975. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3976. **/
  3977. scrollTop: function (scrollTop, duration, easing, callback) {
  3978. },
  3979. /**
  3980. * reset the boundry size
  3981. * @memberof XScroll
  3982. * @return {XScroll}
  3983. **/
  3984. resetSize: function () {
  3985. var self = this;
  3986. if (!self.container || !self.content)
  3987. return;
  3988. var userConfig = self.userConfig;
  3989. var renderToStyle = getComputedStyle(self.renderTo);
  3990. var width = self.width = (userConfig.width || self.renderTo.offsetWidth) - Util.px2Num(renderToStyle['padding-left']) - Util.px2Num(renderToStyle['padding-right']);
  3991. var height = self.height = (userConfig.height || self.renderTo.offsetHeight) - Util.px2Num(renderToStyle['padding-top']) - Util.px2Num(renderToStyle['padding-bottom']);
  3992. var containerWidth = userConfig.containerWidth || self.content.offsetWidth;
  3993. var containerHeight = userConfig.containerHeight || self.content.offsetHeight;
  3994. self.containerWidth = containerWidth < self.width ? self.width : containerWidth;
  3995. self.containerHeight = containerHeight < self.height ? self.height : containerHeight;
  3996. self.boundry.refresh({
  3997. width: self.width,
  3998. height: self.height
  3999. });
  4000. return self;
  4001. },
  4002. /**
  4003. * render scroll
  4004. * @memberof XScroll
  4005. * @return {XScroll}
  4006. **/
  4007. render: function () {
  4008. var self = this;
  4009. self.resetSize();
  4010. //init stickies
  4011. self.initSticky();
  4012. //init fixed elements
  4013. self.initFixed();
  4014. self.trigger('afterrender', { type: 'afterrender' });
  4015. self._bindEvt();
  4016. //update touch-action
  4017. self.initTouchAction();
  4018. return self;
  4019. },
  4020. /**
  4021. * init touch action
  4022. * @memberof XScroll
  4023. * @return {XScroll}
  4024. */
  4025. initTouchAction: function () {
  4026. var self = this;
  4027. self.mc.set({ touchAction: self.userConfig.touchAction });
  4028. return self;
  4029. },
  4030. initFixed: function () {
  4031. var self = this, userConfig = self.userConfig;
  4032. self.fixed = self.fixed || new Fixed({
  4033. fixedElements: userConfig.fixedElements,
  4034. xscroll: self,
  4035. fixedRenderTo: userConfig.fixedRenderTo
  4036. });
  4037. self.fixed.render();
  4038. self.resetSize();
  4039. return self;
  4040. },
  4041. initSticky: function () {
  4042. var self = this, userConfig = self.userConfig;
  4043. var sticky = self.sticky = self.sticky || new Sticky({
  4044. xscroll: self,
  4045. zoomType: userConfig.zoomType,
  4046. stickyRenderTo: userConfig.stickyRenderTo
  4047. });
  4048. sticky.render();
  4049. },
  4050. /**
  4051. * bounce to the boundry vertical and horizontal
  4052. * @memberof XScroll
  4053. * @return {XScroll}
  4054. **/
  4055. boundryCheck: function () {
  4056. return this;
  4057. },
  4058. /**
  4059. * bounce to the boundry horizontal
  4060. * @memberof XScroll
  4061. * @return {XScroll}
  4062. **/
  4063. boundryCheckX: function () {
  4064. return this;
  4065. },
  4066. /**
  4067. * bounce to the boundry vertical
  4068. * @memberof XScroll
  4069. * @return {XScroll}
  4070. **/
  4071. boundryCheckY: function () {
  4072. return this;
  4073. },
  4074. _bindEvt: function () {
  4075. var self = this;
  4076. if (self.___isEvtBind)
  4077. return;
  4078. self.___isEvtBind = true;
  4079. var mc = self.mc = new Hammer.Manager(self.renderTo);
  4080. var tap = new Hammer.Tap();
  4081. var pan = new Hammer.Pan();
  4082. var pinch = new Hammer.Pinch();
  4083. mc.add([
  4084. tap,
  4085. pan
  4086. ]);
  4087. //trigger all events
  4088. self.mc.on('panstart pan panend pancancel pinchstart pinchmove pinchend pinchcancel pinchin pinchout', function (e) {
  4089. self.trigger(e.type, e);
  4090. });
  4091. //trigger touch events
  4092. var touchEvents = [
  4093. 'touchstart',
  4094. 'touchmove',
  4095. 'touchend',
  4096. 'touchcancel',
  4097. 'mousedown'
  4098. ];
  4099. for (var i = 0, l = touchEvents.length; i < l; i++) {
  4100. self.renderTo.addEventListener(touchEvents[i], function (e) {
  4101. self.trigger(e.type, e);
  4102. });
  4103. }
  4104. self.mc.on('tap', function (e) {
  4105. if (e.tapCount == 1) {
  4106. e.type = 'tap';
  4107. self.trigger(e.type, e);
  4108. } else if (e.tapCount == 2) {
  4109. e.type = 'doubletap';
  4110. self.trigger('doubletap', e);
  4111. }
  4112. });
  4113. return self;
  4114. },
  4115. _resetLockConfig: function () {
  4116. },
  4117. stop: function () {
  4118. }
  4119. });
  4120. if (typeof module == 'object' && module.exports) {
  4121. exports = XScroll;
  4122. } /** ignored by jsdoc **/ else {
  4123. return XScroll;
  4124. }
  4125. return exports;
  4126. }(core);
  4127. components_scrollbar = function (exports) {
  4128. var Util = util;
  4129. var Animate = animate;
  4130. var MAX_BOUNCE_DISTANCE = 40;
  4131. var MIN_BAR_SCROLLED_SIZE = 10;
  4132. var MIN_BAR_SIZE = 50;
  4133. var transform = Util.prefixStyle('transform');
  4134. var transformStr = Util.vendor ? [
  4135. '-',
  4136. Util.vendor,
  4137. '-transform'
  4138. ].join('') : 'transform';
  4139. var transition = Util.prefixStyle('transition');
  4140. var borderRadius = Util.prefixStyle('borderRadius');
  4141. var transitionDuration = Util.prefixStyle('transitionDuration');
  4142. var ScrollBar = function (cfg) {
  4143. this.userConfig = Util.mix({
  4144. MIN_BAR_SCROLLED_SIZE: MIN_BAR_SCROLLED_SIZE,
  4145. MIN_BAR_SIZE: MIN_BAR_SIZE,
  4146. MAX_BOUNCE_DISTANCE: MAX_BOUNCE_DISTANCE,
  4147. spacing: 5
  4148. }, cfg);
  4149. this.init(cfg.xscroll);
  4150. };
  4151. Util.mix(ScrollBar.prototype, {
  4152. init: function (xscroll) {
  4153. var self = this;
  4154. self.xscroll = xscroll;
  4155. self.type = self.userConfig.type;
  4156. self.isY = self.type == 'y' ? true : false;
  4157. self.scrollTopOrLeft = self.isY ? 'scrollTop' : 'scrollLeft';
  4158. },
  4159. destroy: function () {
  4160. var self = this;
  4161. Util.remove(self.scrollbar);
  4162. self.xscroll.off('scroll', self._scrollHandler, self);
  4163. self.xscroll.off('scrollend', self._scrollEndHandler, self);
  4164. },
  4165. render: function () {
  4166. var self = this;
  4167. var xscroll = self.xscroll;
  4168. var boundry = xscroll.boundry;
  4169. var indicatorInsets = self.xscroll.userConfig.indicatorInsets;
  4170. var translateZ = xscroll.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4171. var transform = translateZ ? transformStr + ':' + translateZ + ';' : '';
  4172. var commonCss = 'opacity:0;position:absolute;z-index:999;overflow:hidden;-webkit-border-radius:3px;-moz-border-radius:3px;-o-border-radius:3px;' + transform;
  4173. indicatorInsets._xright = indicatorInsets.right + indicatorInsets.spacing;
  4174. indicatorInsets._xbottom = indicatorInsets.bottom + indicatorInsets.spacing;
  4175. var css = self.isY ? Util.substitute('width:{width}px;bottom:{_xbottom}px;top:{top}px;right:{right}px;', indicatorInsets) + commonCss : Util.substitute('height:{width}px;left:{left}px;right:{_xright}px;bottom:{bottom}px;', indicatorInsets) + commonCss;
  4176. if (!self.scrollbar) {
  4177. self.scrollbar = document.createElement('div');
  4178. self.indicate = document.createElement('div');
  4179. xscroll.renderTo.appendChild(self.scrollbar);
  4180. self.scrollbar.appendChild(self.indicate);
  4181. }
  4182. self.scrollbar.style.cssText = css;
  4183. var size = self.isY ? 'width:100%;' : 'height:100%;';
  4184. self.indicate.style.cssText = size + 'position:absolute;background:rgba(0,0,0,0.3);-webkit-border-radius:3px;-moz-border-radius:3px;-o-border-radius:3px;';
  4185. self._update();
  4186. self.hide(0);
  4187. self._bindEvt();
  4188. },
  4189. _update: function (pos, duration, easing, callback) {
  4190. var self = this;
  4191. var pos = undefined === pos ? self.isY ? self.xscroll.getScrollTop() : self.xscroll.getScrollLeft() : pos;
  4192. var barInfo = self.computeScrollBar(pos);
  4193. var size = self.isY ? 'height' : 'width';
  4194. self.indicate.style[size] = Math.round(barInfo.size) + 'px';
  4195. if (duration && easing) {
  4196. self.scrollTo(barInfo.pos, duration, easing, callback);
  4197. } else {
  4198. self.moveTo(barInfo.pos);
  4199. }
  4200. },
  4201. //compute the position and size of the scrollbar
  4202. computeScrollBar: function (pos) {
  4203. var self = this;
  4204. var type = self.isY ? 'y' : 'x';
  4205. var spacing = self.userConfig.spacing;
  4206. var xscroll = self.xscroll;
  4207. var boundry = xscroll.boundry;
  4208. var userConfig = self.userConfig;
  4209. var pos = self.isY ? Math.round(pos) + boundry._xtop : Math.round(pos) + boundry._xleft;
  4210. var MIN_BAR_SCROLLED_SIZE = userConfig.MIN_BAR_SCROLLED_SIZE;
  4211. var MIN_BAR_SIZE = userConfig.MIN_BAR_SIZE;
  4212. var MAX_BOUNCE_DISTANCE = userConfig.MAX_BOUNCE_DISTANCE;
  4213. self.containerSize = self.isY ? xscroll.containerHeight + boundry._xtop + boundry._xbottom : self.xscroll.containerWidth + boundry._xright + boundry._xleft;
  4214. self.size = self.isY ? boundry.cfg.height : boundry.cfg.width;
  4215. self.indicateSize = self.isY ? boundry.cfg.height - spacing * 2 : boundry.cfg.width - spacing * 2;
  4216. var indicateSize = self.indicateSize;
  4217. var containerSize = self.containerSize;
  4218. var barPos = indicateSize * pos / containerSize;
  4219. var barSize = Math.round(indicateSize * self.size / containerSize);
  4220. var overTop = self.isY ? xscroll.getBoundryOutTop() : xscroll.getBoundryOutLeft();
  4221. var overBottom = self.isY ? xscroll.getBoundryOutBottom() : xscroll.getBoundryOutRight();
  4222. var barShiftSize = MIN_BAR_SIZE - barSize > 0 ? MIN_BAR_SIZE - barSize : 0;
  4223. barSize = barSize < MIN_BAR_SIZE ? MIN_BAR_SIZE : barSize;
  4224. barPos = (indicateSize - barShiftSize) * pos / containerSize;
  4225. if (overTop >= 0) {
  4226. var pct = overTop / MAX_BOUNCE_DISTANCE;
  4227. pct = pct > 1 ? 1 : pct;
  4228. barPos = -pct * (barSize - MIN_BAR_SCROLLED_SIZE);
  4229. }
  4230. if (overBottom >= 0) {
  4231. var pct = overBottom / MAX_BOUNCE_DISTANCE;
  4232. pct = pct > 1 ? 1 : pct;
  4233. barPos = pct * (barSize - MIN_BAR_SCROLLED_SIZE) + indicateSize - barSize;
  4234. }
  4235. self.barPos = Math.round(barPos);
  4236. return {
  4237. size: Math.round(barSize),
  4238. pos: self.barPos
  4239. };
  4240. },
  4241. scrollTo: function (pos, duration, easing, callback) {
  4242. var self = this;
  4243. self.show();
  4244. var translateZ = self.xscroll.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4245. var config = {
  4246. css: { transform: self.isY ? 'translateY(' + pos + 'px)' + translateZ : 'translateX(' + pos + 'px)' + translateZ },
  4247. duration: duration,
  4248. easing: easing,
  4249. useTransition: self.xscroll.userConfig.useTransition,
  4250. end: callback
  4251. };
  4252. self.__timer = self.__timer || new Animate(self.indicate, config);
  4253. //run
  4254. self.__timer.stop();
  4255. self.__timer.reset(config);
  4256. self.__timer.run();
  4257. },
  4258. moveTo: function (pos) {
  4259. var self = this;
  4260. self.show();
  4261. var translateZ = self.xscroll.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4262. self.isY ? self.indicate.style[transform] = 'translateY(' + pos + 'px) ' + translateZ : self.indicate.style[transform] = 'translateX(' + pos + 'px) ' + translateZ;
  4263. self.indicate.style[transition] = '';
  4264. },
  4265. _scrollHandler: function (e) {
  4266. var self = this;
  4267. self._update(e[self.scrollTopOrLeft]);
  4268. return self;
  4269. },
  4270. isBoundryOut: function () {
  4271. var self = this;
  4272. return !self.isY ? self.xscroll.isBoundryOutLeft() || self.xscroll.isBoundryOutRight() : self.xscroll.isBoundryOutTop() || self.xscroll.isBoundryOutBottom();
  4273. },
  4274. _scrollEndHandler: function (e) {
  4275. var self = this;
  4276. if (!self.isBoundryOut()) {
  4277. self._update(e[self.scrollTopOrLeft]);
  4278. self.hide();
  4279. }
  4280. return self;
  4281. },
  4282. _bindEvt: function () {
  4283. var self = this;
  4284. if (self.__isEvtBind)
  4285. return;
  4286. self.__isEvtBind = true;
  4287. self.xscroll.on('scroll', self._scrollHandler, self);
  4288. self.xscroll.on('scrollend', self._scrollEndHandler, self);
  4289. },
  4290. reset: function () {
  4291. var self = this;
  4292. self.pos = 0;
  4293. self._update();
  4294. },
  4295. hide: function (duration, easing, delay) {
  4296. var self = this;
  4297. var duration = duration >= 0 ? duration : 300;
  4298. var easing = easing || 'ease-out';
  4299. var delay = delay >= 0 ? delay : 100;
  4300. self.scrollbar.style.opacity = 0;
  4301. self.scrollbar.style[transition] = [
  4302. 'opacity ',
  4303. duration,
  4304. 'ms ',
  4305. ' ease-out ',
  4306. delay,
  4307. 'ms'
  4308. ].join('');
  4309. },
  4310. show: function () {
  4311. var self = this;
  4312. self.scrollbar.style.opacity = 1;
  4313. self.scrollbar.style[transition] = '';
  4314. }
  4315. });
  4316. if (typeof module == 'object' && module.exports) {
  4317. exports = ScrollBar;
  4318. } /** ignored by jsdoc **/ else {
  4319. return ScrollBar;
  4320. }
  4321. return exports;
  4322. }(components_scrollbar);
  4323. components_controller = function (exports) {
  4324. var Util = util, Base = base;
  4325. var Controller = function (cfg) {
  4326. Controller.superclass.constructor.call(this, cfg);
  4327. this.userConfig = Util.mix({}, cfg);
  4328. this.init();
  4329. };
  4330. Util.extend(Controller, Base, {
  4331. init: function () {
  4332. var self = this;
  4333. self.xscroll = self.userConfig.xscroll;
  4334. },
  4335. add: function (scroll, cfg) {
  4336. var self = this;
  4337. cfg = Util.extend({
  4338. captureBounce: false,
  4339. stopPropagation: true
  4340. }, cfg);
  4341. if (!self.__scrolls) {
  4342. self.__scrolls = {};
  4343. }
  4344. if (scroll.guid && !self.__scrolls[scroll.guid]) {
  4345. scroll.parentscroll = self.xscroll;
  4346. self._bind(scroll);
  4347. return self.__scrolls[scroll.guid] = scroll;
  4348. }
  4349. return;
  4350. },
  4351. remove: function (scroll) {
  4352. var self = this;
  4353. if (!scroll || !scroll.guid)
  4354. return;
  4355. var subscroll = self.__scrolls[scroll.guid];
  4356. if (subscroll) {
  4357. subscroll.parentscroll = null;
  4358. self._unbind(scroll);
  4359. subscroll = null;
  4360. }
  4361. },
  4362. get: function (guid) {
  4363. if (guid) {
  4364. return this.__scrolls[guid];
  4365. }
  4366. return this.__scrolls;
  4367. },
  4368. _unbind: function (sub) {
  4369. },
  4370. _bind: function (sub) {
  4371. var self = this, xscroll = self.xscroll;
  4372. xscroll.renderTo.addEventListener('touchstart', function () {
  4373. xscroll._resetLockConfig();
  4374. });
  4375. sub.renderTo.addEventListener('touchstart', function () {
  4376. sub._resetLockConfig();
  4377. });
  4378. xscroll.on('panend', xscroll._resetLockConfig);
  4379. sub.on('panend', sub._resetLockConfig);
  4380. sub.on('panstart', function (e) {
  4381. //vertical scroll enabled
  4382. if (!sub.userConfig.lockY && !xscroll.userConfig.lockY) {
  4383. //outside of boundry
  4384. if (sub.isBoundryOut()) {
  4385. xscroll.userConfig.lockY = true;
  4386. return;
  4387. }
  4388. if (e.direction == 16 && sub.getBoundryOutTop() >= 0) {
  4389. sub.userConfig.lockY = true;
  4390. } else if (e.direction == 8 && sub.getBoundryOutTop() >= 0 && sub.getBoundryOutBottom() < 0) {
  4391. xscroll.userConfig.lockY = true;
  4392. }
  4393. if (e.direction == 8 && sub.getBoundryOutBottom() >= 0) {
  4394. sub.userConfig.lockY = true;
  4395. } else if (e.direction == 16 && sub.getBoundryOutBottom() >= 0 && sub.getBoundryOutTop() < 0) {
  4396. xscroll.userConfig.lockY = true;
  4397. }
  4398. if (sub.getBoundryOutTop() < 0 && sub.getBoundryOutBottom() < 0) {
  4399. xscroll.userConfig.lockY = true;
  4400. }
  4401. }
  4402. //horizontal scroll enabled
  4403. if (!sub.userConfig.lockX && !xscroll.userConfig.lockX) {
  4404. if (sub.isBoundryOut()) {
  4405. xscroll.userConfig.lockX = true;
  4406. return;
  4407. }
  4408. if (e.direction == 4 && sub.getBoundryOutLeft() >= 0) {
  4409. sub.userConfig.lockX = true;
  4410. } else if (e.direction == 2 && sub.getBoundryOutLeft() >= 0 && sub.getBoundryOutRight() < 0) {
  4411. xscroll.userConfig.lockX = true;
  4412. }
  4413. if (e.direction == 2 && sub.getBoundryOutRight() >= 0) {
  4414. sub.userConfig.lockX = true;
  4415. } else if (e.direction == 4 && sub.getBoundryOutRight() >= 0 && sub.getBoundryOutLeft() < 0) {
  4416. xscroll.userConfig.lockX = true;
  4417. }
  4418. if (sub.getBoundryOutLeft() < 0 && sub.getBoundryOutRight() < 0) {
  4419. xscroll.userConfig.lockX = true;
  4420. }
  4421. }
  4422. if (!sub.userConfig.lockX && xscroll.userConfig.lockX) {
  4423. //pan x
  4424. if (e.direction == 2 || e.direction == 4) {
  4425. xscroll.userConfig.lockY = true;
  4426. } else {
  4427. sub.userConfig.lockX = true;
  4428. }
  4429. }
  4430. if (!sub.userConfig.lockY && xscroll.userConfig.lockY) {
  4431. //pan y
  4432. if (e.direction == 8 || e.direction == 16) {
  4433. xscroll.userConfig.lockX = true;
  4434. } else {
  4435. sub.userConfig.lockY = true;
  4436. }
  4437. }
  4438. });
  4439. }
  4440. });
  4441. if (typeof module == 'object' && module.exports) {
  4442. exports = Controller;
  4443. } /** ignored by jsdoc **/ else {
  4444. return Controller;
  4445. }
  4446. return exports;
  4447. }(components_controller);
  4448. simulate_scroll = function (exports) {
  4449. var Util = util, Base = base, Core = core, Animate = animate, Hammer = hammer, ScrollBar = components_scrollbar, Controller = components_controller;
  4450. //reduced boundry pan distance
  4451. var PAN_RATE = 1 - 0.618;
  4452. //constant for scrolling acceleration
  4453. var SCROLL_ACCELERATION = 0.0005;
  4454. //constant for outside of boundry acceleration
  4455. var BOUNDRY_ACCELERATION = 0.03;
  4456. //transform-origin
  4457. var transformOrigin = Util.prefixStyle('transformOrigin');
  4458. //transform
  4459. var transform = Util.prefixStyle('transform');
  4460. /**
  4461. * @constructor
  4462. * @param {object} cfg config for scroll
  4463. * @param {number} cfg.SCROLL_ACCELERATION acceleration for scroll, min value make the scrolling smoothly
  4464. * @param {number} cfg.BOUNDRY_CHECK_DURATION duration for boundry bounce
  4465. * @param {number} cfg.BOUNDRY_CHECK_EASING easing for boundry bounce
  4466. * @param {number} cfg.BOUNDRY_CHECK_ACCELERATION acceleration for boundry bounce
  4467. * @param {boolean} cfg.lockX just like overflow-x:hidden
  4468. * @param {boolean} cfg.lockY just like overflow-y:hidden
  4469. * @param {boolean} cfg.scrollbarX config if the scrollbar-x is visible
  4470. * @param {boolean} cfg.scrollbarY config if the scrollbar-y is visible
  4471. * @param {boolean} cfg.useTransition config if use css3 transition or raf for scroll animation
  4472. * @param {boolean} cfg.bounce config if use has the bounce effect when scrolling outside of the boundry
  4473. * @param {boolean} cfg.boundryCheck config if scrolling inside of the boundry
  4474. * @param {boolean} cfg.preventDefault prevent touchstart
  4475. * @param {boolean} cfg.preventTouchMove prevent touchmove
  4476. * @param {string|HTMLElement} cfg.container config for scroller's container which default value is ".xs-container"
  4477. * @param {string|HTMLElement} cfg.content config for scroller's content which default value is ".xs-content"
  4478. * @param {object} cfg.indicatorInsets config scrollbars position {top: number, left: number, bottom: number, right: number}
  4479. * @param {string} cfg.stickyElements config for sticky-positioned elements
  4480. * @param {string} cfg.fixedElements config for fixed-positioned elements
  4481. * @param {string} cfg.touchAction config for touchAction of the scroller
  4482. * @extends XScroll
  4483. * @example
  4484. * var xscroll = new SimuScroll({
  4485. * renderTo:"#scroll",
  4486. * lockX:false,
  4487. * scrollbarX:true
  4488. * });
  4489. * xscroll.render();
  4490. */
  4491. function SimuScroll(cfg) {
  4492. SimuScroll.superclass.constructor.call(this, cfg);
  4493. }
  4494. Util.extend(SimuScroll, Core, {
  4495. /**
  4496. * @memberof SimuScroll
  4497. * @override
  4498. */
  4499. init: function () {
  4500. var self = this;
  4501. var defaultCfg = {
  4502. preventDefault: true,
  4503. preventTouchMove: true
  4504. };
  4505. SimuScroll.superclass.init.call(this);
  4506. self.userConfig = Util.mix(defaultCfg, self.userConfig);
  4507. self.SCROLL_ACCELERATION = self.userConfig.SCROLL_ACCELERATION || SCROLL_ACCELERATION;
  4508. self.BOUNDRY_ACCELERATION = self.userConfig.BOUNDRY_ACCELERATION || BOUNDRY_ACCELERATION;
  4509. self._initContainer();
  4510. self.resetSize();
  4511. //set overflow behaviors
  4512. self._setOverflowBehavior();
  4513. self.defaltConfig = {
  4514. lockY: self.userConfig.lockY,
  4515. lockX: self.userConfig.lockX
  4516. };
  4517. return self;
  4518. },
  4519. destroy: function () {
  4520. var self = this;
  4521. SimuScroll.superclass.destroy.call(this);
  4522. self.renderTo.style.overflow = '';
  4523. self.renderTo.style.touchAction = '';
  4524. self.container.style.transform = '';
  4525. self.container.style.transformOrigin = '';
  4526. self.content.style.transform = '';
  4527. self.content.style.transformOrigin = '';
  4528. self.off('touchstart mousedown', self._ontouchstart);
  4529. self.off('touchmove', self._ontouchmove);
  4530. window.removeEventListener('resize', self.resizeHandler, self);
  4531. self.destroyScrollBars();
  4532. },
  4533. /**
  4534. * set overflow behavior
  4535. * @return {boolean} [description]
  4536. */
  4537. _setOverflowBehavior: function () {
  4538. var self = this;
  4539. var renderTo = self.renderTo;
  4540. var computeStyle = getComputedStyle(renderTo);
  4541. self.userConfig.lockX = undefined === self.userConfig.lockX ? computeStyle['overflow-x'] == 'hidden' || self.width == self.containerWidth ? true : false : self.userConfig.lockX;
  4542. self.userConfig.lockY = undefined === self.userConfig.lockY ? computeStyle['overflow-y'] == 'hidden' || self.height == self.containerHeight ? true : false : self.userConfig.lockY;
  4543. self.userConfig.scrollbarX = undefined === self.userConfig.scrollbarX ? self.userConfig.lockX ? false : true : self.userConfig.scrollbarX;
  4544. self.userConfig.scrollbarY = undefined === self.userConfig.scrollbarY ? self.userConfig.lockY ? false : true : self.userConfig.scrollbarY;
  4545. return self;
  4546. },
  4547. /**
  4548. * reset lockX or lockY config to the default value
  4549. */
  4550. _resetLockConfig: function () {
  4551. var self = this;
  4552. self.userConfig.lockX = self.defaltConfig.lockX;
  4553. self.userConfig.lockY = self.defaltConfig.lockY;
  4554. return self;
  4555. },
  4556. /**
  4557. * init container
  4558. * @override
  4559. * @return {SimuScroll}
  4560. */
  4561. _initContainer: function () {
  4562. var self = this;
  4563. SimuScroll.superclass._initContainer.call(self);
  4564. if (self.__isContainerInited || !self.container || !self.content)
  4565. return;
  4566. self.container.style[transformOrigin] = '0 0';
  4567. self.content.style[transformOrigin] = '0 0';
  4568. self.translate(0, 0);
  4569. self.__isContainerInited = true;
  4570. return self;
  4571. },
  4572. /**
  4573. * get scroll top value
  4574. * @memberof SimuScroll
  4575. * @return {number} scrollTop
  4576. */
  4577. getScrollTop: function () {
  4578. var transY = window.getComputedStyle(this.container)[transform].match(/[-\d\.*\d*]+/g);
  4579. return transY ? Math.round(transY[5]) === 0 ? 0 : -Math.round(transY[5]) : 0;
  4580. },
  4581. /**
  4582. * get scroll left value
  4583. * @memberof SimuScroll
  4584. * @return {number} scrollLeft
  4585. */
  4586. getScrollLeft: function () {
  4587. var transX = window.getComputedStyle(this.content)[transform].match(/[-\d\.*\d*]+/g);
  4588. return transX ? Math.round(transX[4]) === 0 ? 0 : -Math.round(transX[4]) : 0;
  4589. },
  4590. /**
  4591. * horizontal scroll absolute to the destination
  4592. * @memberof SimuScroll
  4593. * @param scrollLeft {number} scrollLeft
  4594. * @param duration {number} duration for animte
  4595. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  4596. **/
  4597. scrollLeft: function (x, duration, easing, callback) {
  4598. if (this.userConfig.lockX)
  4599. return;
  4600. var translateZ = this.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4601. this.x = undefined === x || isNaN(x) || 0 === x ? 0 : -Math.round(x);
  4602. this._animate('x', 'translateX(' + this.x + 'px) scale(' + this.scale + ')' + translateZ, duration, easing, callback);
  4603. return this;
  4604. },
  4605. /**
  4606. * vertical scroll absolute to the destination
  4607. * @memberof SimuScroll
  4608. * @param scrollTop {number} scrollTop
  4609. * @param duration {number} duration for animte
  4610. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  4611. **/
  4612. scrollTop: function (y, duration, easing, callback) {
  4613. if (this.userConfig.lockY)
  4614. return;
  4615. var translateZ = this.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4616. this.y = undefined === y || isNaN(y) || 0 === y ? 0 : -Math.round(y);
  4617. this._animate('y', 'translateY(' + this.y + 'px) ' + translateZ, duration, easing, callback);
  4618. return this;
  4619. },
  4620. /**
  4621. * translate the scroller to a new destination includes x , y , scale
  4622. * @memberof SimuScroll
  4623. * @param x {number} x
  4624. * @param y {number} y
  4625. * @param scale {number} scale
  4626. **/
  4627. translate: function (x, y, scale) {
  4628. var translateZ = this.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4629. this.x = x || this.x || 0;
  4630. this.y = y || this.y || 0;
  4631. this.scale = scale || this.scale || 1;
  4632. this.content.style[transform] = 'translate(' + this.x + 'px,0px) scale(' + this.scale + ') ' + translateZ;
  4633. this.container.style[transform] = 'translate(0px,' + this.y + 'px) ' + translateZ;
  4634. return this;
  4635. },
  4636. _animate: function (type, transform, duration, easing, callback) {
  4637. var self = this;
  4638. var duration = duration || 0;
  4639. var easing = easing || 'quadratic';
  4640. var el = type == 'y' ? self.container : self.content;
  4641. var config = {
  4642. css: { transform: transform },
  4643. duration: duration,
  4644. easing: easing,
  4645. run: function (e) {
  4646. /**
  4647. * @event {@link SimuScroll#"scroll"}
  4648. */
  4649. self.trigger('scroll', {
  4650. scrollTop: self.getScrollTop(),
  4651. scrollLeft: self.getScrollLeft(),
  4652. type: 'scroll'
  4653. });
  4654. },
  4655. useTransition: self.userConfig.useTransition,
  4656. end: function (e) {
  4657. callback && callback();
  4658. if ((self['_bounce' + type] === 0 || self['_bounce' + type] === undefined) && easing != 'linear') {
  4659. self['isScrolling' + type.toUpperCase()] = false;
  4660. self['isRealScrolling' + type.toUpperCase()] = false;
  4661. self.trigger('scrollend', {
  4662. type: 'scrollend',
  4663. scrollTop: self.getScrollTop(),
  4664. scrollLeft: self.getScrollLeft(),
  4665. zoomType: type,
  4666. duration: duration,
  4667. easing: easing
  4668. });
  4669. }
  4670. }
  4671. };
  4672. var timer = self.__timers[type] = self.__timers[type] || new Animate(el, config);
  4673. timer.stop();
  4674. timer.reset(config);
  4675. timer.run();
  4676. self.trigger('scrollanimate', {
  4677. type: 'scrollanimate',
  4678. scrollTop: -self.y,
  4679. scrollLeft: -self.x,
  4680. duration: duration,
  4681. easing: easing,
  4682. zoomType: type
  4683. });
  4684. return this;
  4685. },
  4686. _ontap: function (e) {
  4687. var self = this;
  4688. self.boundryCheck();
  4689. // self._unPreventHref(e);
  4690. if (!self.isRealScrollingX && !self.isRealScrollingY) {
  4691. }
  4692. // self._preventHref(e);
  4693. self.isRealScrollingY = false;
  4694. self.isRealScrollingY = false;
  4695. },
  4696. _bindEvt: function () {
  4697. SimuScroll.superclass._bindEvt.call(this);
  4698. var self = this;
  4699. if (self.__isEvtBind)
  4700. return;
  4701. self.__isEvtBind = true;
  4702. var pinch = new Hammer.Pinch();
  4703. self.mc.add(pinch);
  4704. self.on('touchstart mousedown', self._ontouchstart, self);
  4705. self.on('touchmove', self._ontouchmove, self);
  4706. self.on('tap', self._ontap, self);
  4707. self.on('panstart', self._onpanstart, self);
  4708. self.on('pan', self._onpan, self);
  4709. self.on('panend', self._onpanend, self);
  4710. self.resizeHandler = function (e) {
  4711. setTimeout(function () {
  4712. self.resetSize();
  4713. self.boundryCheck(0);
  4714. self.render();
  4715. }, 100);
  4716. };
  4717. //window resize
  4718. window.addEventListener('resize', self.resizeHandler, self);
  4719. return this;
  4720. },
  4721. _ontouchstart: function (e) {
  4722. var self = this;
  4723. if (!/(SELECT|INPUT|TEXTAREA)/i.test(e.target.tagName) && self.userConfig.preventDefault) {
  4724. e.preventDefault();
  4725. }
  4726. self.stop();
  4727. },
  4728. _ontouchmove: function (e) {
  4729. this.userConfig.preventTouchMove && e.preventDefault();
  4730. },
  4731. _onpanstart: function (e) {
  4732. this.userConfig.preventTouchMove && e.preventDefault();
  4733. var self = this;
  4734. var scrollLeft = self.getScrollLeft();
  4735. var scrollTop = self.getScrollTop();
  4736. self.stop();
  4737. self.translate(-scrollLeft, -scrollTop);
  4738. var threshold = self.mc.get('pan').options.threshold;
  4739. self.thresholdY = e.direction == '8' ? threshold : e.direction == '16' ? -threshold : 0;
  4740. self.thresholdX = e.direction == '2' ? threshold : e.direction == '4' ? -threshold : 0;
  4741. return self;
  4742. },
  4743. _onpan: function (e) {
  4744. this.userConfig.preventTouchMove && e.preventDefault();
  4745. var self = this;
  4746. var boundry = self.boundry;
  4747. var userConfig = self.userConfig;
  4748. var boundryCheck = userConfig.boundryCheck;
  4749. var bounce = userConfig.bounce;
  4750. var scrollTop = self.__topstart || (self.__topstart = -self.getScrollTop());
  4751. var scrollLeft = self.__leftstart || (self.__leftstart = -self.getScrollLeft());
  4752. var y = userConfig.lockY ? Number(scrollTop) : Number(scrollTop) + (e.deltaY + self.thresholdY);
  4753. var x = userConfig.lockX ? Number(scrollLeft) : Number(scrollLeft) + (e.deltaX + self.thresholdX);
  4754. var containerWidth = self.containerWidth;
  4755. var containerHeight = self.containerHeight;
  4756. if (boundryCheck) {
  4757. //over top
  4758. y = y > boundry.top ? bounce ? (y - boundry.top) * PAN_RATE + boundry.top : boundry.top : y;
  4759. //over bottom
  4760. y = y < boundry.bottom - containerHeight ? bounce ? y + (boundry.bottom - containerHeight - y) * PAN_RATE : boundry.bottom - containerHeight : y;
  4761. //over left
  4762. x = x > boundry.left ? bounce ? (x - boundry.left) * PAN_RATE + boundry.left : boundry.left : x;
  4763. //over right
  4764. x = x < boundry.right - containerWidth ? bounce ? x + (boundry.right - containerWidth - x) * PAN_RATE : boundry.right - containerWidth : x;
  4765. }
  4766. //move to x,y
  4767. self.translate(x, y);
  4768. //pan trigger the opposite direction
  4769. self.directionX = e.type == 'panleft' ? 'right' : e.type == 'panright' ? 'left' : '';
  4770. self.directionY = e.type == 'panup' ? 'down' : e.type == 'pandown' ? 'up' : '';
  4771. self.trigger('scroll', {
  4772. scrollTop: -y,
  4773. scrollLeft: -x,
  4774. triggerType: 'pan',
  4775. type: 'scroll'
  4776. });
  4777. return self;
  4778. },
  4779. _onpanend: function (e) {
  4780. var self = this;
  4781. var userConfig = self.userConfig;
  4782. var transX = self.computeScroll('x', e.velocityX);
  4783. var transY = self.computeScroll('y', e.velocityY);
  4784. var scrollLeft = transX ? transX.pos : 0;
  4785. var scrollTop = transY ? transY.pos : 0;
  4786. var duration;
  4787. if (transX && transY && transX.status == 'inside' && transY.status == 'inside' && transX.duration && transY.duration) {
  4788. //ensure the same duration
  4789. duration = Math.max(transX.duration, transY.duration);
  4790. }
  4791. transX && self.scrollLeft(scrollLeft, duration || transX.duration, transX.easing, function (e) {
  4792. self.boundryCheckX();
  4793. });
  4794. transY && self.scrollTop(scrollTop, duration || transY.duration, transY.easing, function (e) {
  4795. self.boundryCheckY();
  4796. });
  4797. //judge the direction
  4798. self.directionX = e.velocityX < 0 ? 'left' : 'right';
  4799. self.directionY = e.velocityY < 0 ? 'up' : 'down';
  4800. //clear start
  4801. self.__topstart = null;
  4802. self.__leftstart = null;
  4803. return self;
  4804. },
  4805. /**
  4806. * judge the scroller is out of boundry horizontally and vertically
  4807. * @memberof SimuScroll
  4808. * @return {boolean} isBoundryOut
  4809. **/
  4810. isBoundryOut: function () {
  4811. return this.isBoundryOutLeft() || this.isBoundryOutRight() || this.isBoundryOutTop() || this.isBoundryOutBottom();
  4812. },
  4813. /**
  4814. * judge if the scroller is outsideof left
  4815. * @memberof SimuScroll
  4816. * @return {boolean} isBoundryOut
  4817. **/
  4818. isBoundryOutLeft: function () {
  4819. return this.getBoundryOutLeft() > 0 ? true : false;
  4820. },
  4821. /**
  4822. * judge if the scroller is outsideof right
  4823. * @memberof SimuScroll
  4824. * @return {boolean} isBoundryOut
  4825. **/
  4826. isBoundryOutRight: function () {
  4827. return this.getBoundryOutRight() > 0 ? true : false;
  4828. },
  4829. /**
  4830. * judge if the scroller is outsideof top
  4831. * @memberof SimuScroll
  4832. * @return {boolean} isBoundryOut
  4833. **/
  4834. isBoundryOutTop: function () {
  4835. return this.getBoundryOutTop() > 0 ? true : false;
  4836. },
  4837. /**
  4838. * judge if the scroller is outsideof bottom
  4839. * @memberof SimuScroll
  4840. * @return {boolean} isBoundryOut
  4841. **/
  4842. isBoundryOutBottom: function () {
  4843. return this.getBoundryOutBottom() > 0 ? true : false;
  4844. },
  4845. /**
  4846. * get the offset value outsideof top
  4847. * @memberof SimuScroll
  4848. * @return {number} offset
  4849. **/
  4850. getBoundryOutTop: function () {
  4851. return -this.boundry.top - this.getScrollTop();
  4852. },
  4853. /**
  4854. * get the offset value outsideof left
  4855. * @memberof SimuScroll
  4856. * @return {number} offset
  4857. **/
  4858. getBoundryOutLeft: function () {
  4859. return -this.boundry.left - this.getScrollLeft();
  4860. },
  4861. /**
  4862. * get the offset value outsideof bottom
  4863. * @memberof SimuScroll
  4864. * @return {number} offset
  4865. **/
  4866. getBoundryOutBottom: function () {
  4867. return this.boundry.bottom - this.containerHeight + this.getScrollTop();
  4868. },
  4869. /**
  4870. * get the offset value outsideof right
  4871. * @memberof SimuScroll
  4872. * @return {number} offset
  4873. **/
  4874. getBoundryOutRight: function () {
  4875. return this.boundry.right - this.containerWidth + this.getScrollLeft();
  4876. },
  4877. /**
  4878. * compute scroll transition by zoomType and velocity
  4879. * @memberof SimuScroll
  4880. * @param {string} zoomType zoomType of scrolling
  4881. * @param {number} velocity velocity after panend
  4882. * @example
  4883. * var info = xscroll.computeScroll("x",2);
  4884. * // return {pos:90,easing:"easing",status:"inside",duration:500}
  4885. * @return {Object}
  4886. **/
  4887. computeScroll: function (type, v) {
  4888. var self = this;
  4889. var userConfig = self.userConfig;
  4890. var boundry = self.boundry;
  4891. var pos = type == 'x' ? self.getScrollLeft() : self.getScrollTop();
  4892. var boundryStart = type == 'x' ? boundry.left : boundry.top;
  4893. var boundryEnd = type == 'x' ? boundry.right : boundry.bottom;
  4894. var innerSize = type == 'x' ? self.containerWidth : self.containerHeight;
  4895. var maxSpeed = userConfig.maxSpeed || 2;
  4896. var boundryCheck = userConfig.boundryCheck;
  4897. var bounce = userConfig.bounce;
  4898. var transition = {};
  4899. var status = 'inside';
  4900. if (boundryCheck) {
  4901. if (type == 'x' && (self.isBoundryOutLeft() || self.isBoundryOutRight())) {
  4902. self.boundryCheckX();
  4903. return;
  4904. } else if (type == 'y' && (self.isBoundryOutTop() || self.isBoundryOutBottom())) {
  4905. self.boundryCheckY();
  4906. return;
  4907. }
  4908. }
  4909. if (type == 'x' && self.userConfig.lockX)
  4910. return;
  4911. if (type == 'y' && self.userConfig.lockY)
  4912. return;
  4913. v = v > maxSpeed ? maxSpeed : v < -maxSpeed ? -maxSpeed : v;
  4914. var a = self.SCROLL_ACCELERATION * (v / (Math.abs(v) || 1));
  4915. var a2 = self.BOUNDRY_ACCELERATION;
  4916. var t = isNaN(v / a) ? 0 : v / a;
  4917. var s = Number(pos) + t * v / 2;
  4918. //over top boundry check bounce
  4919. if (s < -boundryStart && boundryCheck) {
  4920. var _s = -boundryStart - pos;
  4921. var _t = (Math.sqrt(-2 * a * _s + v * v) + v) / a;
  4922. var v0 = v - a * _t;
  4923. var _t2 = Math.abs(v0 / a2);
  4924. var s2 = v0 / 2 * _t2;
  4925. t = _t + _t2;
  4926. s = bounce ? -boundryStart + s2 : -boundryStart;
  4927. status = 'outside';
  4928. } else if (s > innerSize - boundryEnd && boundryCheck) {
  4929. var _s = boundryEnd - innerSize + pos;
  4930. var _t = (Math.sqrt(-2 * a * _s + v * v) - v) / a;
  4931. var v0 = v - a * _t;
  4932. var _t2 = Math.abs(v0 / a2);
  4933. var s2 = v0 / 2 * _t2;
  4934. t = _t + _t2;
  4935. s = bounce ? innerSize - boundryEnd + s2 : innerSize - boundryEnd;
  4936. status = 'outside';
  4937. }
  4938. if (isNaN(s) || isNaN(t))
  4939. return;
  4940. transition.pos = s;
  4941. transition.duration = t;
  4942. transition.easing = Math.abs(v) > 2 ? 'circular' : 'quadratic';
  4943. transition.status = status;
  4944. var Type = type.toUpperCase();
  4945. self['isScrolling' + Type] = true;
  4946. self['isRealScrolling' + Type] = true;
  4947. return transition;
  4948. },
  4949. /**
  4950. * bounce to the boundry horizontal
  4951. * @memberof SimuScroll
  4952. * @return {SimuScroll}
  4953. **/
  4954. boundryCheckX: function (duration, easing, callback) {
  4955. var self = this;
  4956. if (!self.userConfig.boundryCheck)
  4957. return;
  4958. if (typeof arguments[0] == 'function') {
  4959. callback = arguments[0];
  4960. duration = self.userConfig.BOUNDRY_CHECK_DURATION;
  4961. easing = self.userConfig.BOUNDRY_CHECK_EASING;
  4962. } else {
  4963. duration = duration === 0 ? 0 : self.userConfig.BOUNDRY_CHECK_DURATION, easing = easing || self.userConfig.BOUNDRY_CHECK_EASING;
  4964. }
  4965. if (!self.userConfig.bounce || self.userConfig.lockX)
  4966. return;
  4967. var boundry = self.boundry;
  4968. if (self.isBoundryOutLeft()) {
  4969. self.scrollLeft(-boundry.left, duration, easing, callback);
  4970. } else if (self.isBoundryOutRight()) {
  4971. self.scrollLeft(self.containerWidth - boundry.right, duration, easing, callback);
  4972. }
  4973. return self;
  4974. },
  4975. /**
  4976. * bounce to the boundry vertical
  4977. * @memberof SimuScroll
  4978. * @return {SimuScroll}
  4979. **/
  4980. boundryCheckY: function (duration, easing, callback) {
  4981. var self = this;
  4982. if (!self.userConfig.boundryCheck)
  4983. return;
  4984. if (typeof arguments[0] == 'function') {
  4985. callback = arguments[0];
  4986. duration = self.userConfig.BOUNDRY_CHECK_DURATION;
  4987. easing = self.userConfig.BOUNDRY_CHECK_EASING;
  4988. } else {
  4989. duration = duration === 0 ? 0 : self.userConfig.BOUNDRY_CHECK_DURATION, easing = easing || self.userConfig.BOUNDRY_CHECK_EASING;
  4990. }
  4991. if (!self.userConfig.boundryCheck || self.userConfig.lockY)
  4992. return;
  4993. var boundry = self.boundry;
  4994. if (self.isBoundryOutTop()) {
  4995. self.scrollTop(-boundry.top, duration, easing, callback);
  4996. } else if (self.isBoundryOutBottom()) {
  4997. self.scrollTop(self.containerHeight - boundry.bottom, duration, easing, callback);
  4998. }
  4999. return self;
  5000. },
  5001. /**
  5002. * bounce to the boundry vertical and horizontal
  5003. * @memberof SimuScroll
  5004. * @return {SimuScroll}
  5005. **/
  5006. boundryCheck: function (duration, easing, callback) {
  5007. this.boundryCheckX(duration, easing, callback);
  5008. this.boundryCheckY(duration, easing, callback);
  5009. return this;
  5010. },
  5011. /**
  5012. * stop scrolling immediatelly
  5013. * @memberof SimuScroll
  5014. * @return {SimuScroll}
  5015. **/
  5016. stop: function () {
  5017. var self = this;
  5018. self.__timers.x && self.__timers.x.stop();
  5019. self.__timers.y && self.__timers.y.stop();
  5020. if (self.isScrollingX || self.isScrollingY) {
  5021. var scrollTop = self.getScrollTop(), scrollLeft = self.getScrollLeft();
  5022. self.trigger('scrollend', {
  5023. scrollTop: scrollTop,
  5024. scrollLeft: scrollLeft
  5025. });
  5026. self.trigger('stop', {
  5027. scrollTop: scrollTop,
  5028. scrollLeft: scrollLeft
  5029. });
  5030. self.isScrollingX = false;
  5031. self.isScrollingY = false;
  5032. }
  5033. return self;
  5034. },
  5035. /**
  5036. * render scroll
  5037. * @memberof SimuScroll
  5038. * @return {SimuScroll}
  5039. **/
  5040. render: function () {
  5041. var self = this;
  5042. SimuScroll.superclass.render.call(this);
  5043. //fixed for scrollbars
  5044. if (getComputedStyle(self.renderTo).position == 'static') {
  5045. self.renderTo.style.position = 'relative';
  5046. }
  5047. self.renderTo.style.overflow = 'hidden';
  5048. self.initScrollBars();
  5049. self.initController();
  5050. return self;
  5051. },
  5052. /**
  5053. * init scrollbars
  5054. * @memberof SimuScroll
  5055. * @return {SimuScroll}
  5056. */
  5057. initScrollBars: function () {
  5058. var self = this;
  5059. if (!self.userConfig.boundryCheck)
  5060. return;
  5061. var indicatorInsets = self.userConfig.indicatorInsets;
  5062. if (self.userConfig.scrollbarX) {
  5063. self.scrollbarX = self.scrollbarX || new ScrollBar({
  5064. xscroll: self,
  5065. type: 'x',
  5066. spacing: indicatorInsets.spacing
  5067. });
  5068. self.scrollbarX.render();
  5069. self.scrollbarX._update();
  5070. self.scrollbarX.hide();
  5071. }
  5072. if (self.userConfig.scrollbarY) {
  5073. self.scrollbarY = self.scrollbarY || new ScrollBar({
  5074. xscroll: self,
  5075. type: 'y',
  5076. spacing: indicatorInsets.spacing
  5077. });
  5078. self.scrollbarY.render();
  5079. self.scrollbarY._update();
  5080. self.scrollbarY.hide();
  5081. }
  5082. return self;
  5083. },
  5084. /**
  5085. * destroy scrollbars
  5086. * @memberof SimuScroll
  5087. * @return {SimuScroll}
  5088. */
  5089. destroyScrollBars: function () {
  5090. this.scrollbarX && this.scrollbarX.destroy();
  5091. this.scrollbarY && this.scrollbarY.destroy();
  5092. return this;
  5093. },
  5094. /**
  5095. * init controller for multi-scrollers
  5096. * @memberof SimuScroll
  5097. * @return {SimuScroll}
  5098. */
  5099. initController: function () {
  5100. var self = this;
  5101. self.controller = self.controller || new Controller({ xscroll: self });
  5102. return self;
  5103. },
  5104. _unPreventHref: function (e) {
  5105. var target = Util.findParentEl(e.target, 'a', this.renderTo);
  5106. if (!target)
  5107. return;
  5108. if (target.tagName.toLowerCase() == 'a') {
  5109. var href = target.getAttribute('data-xs-href');
  5110. if (href) {
  5111. target.setAttribute('href', href);
  5112. }
  5113. }
  5114. },
  5115. _preventHref: function (e) {
  5116. var target = Util.findParentEl(e.target, 'a', this.renderTo);
  5117. if (!target)
  5118. return;
  5119. if (target.tagName.toLowerCase() == 'a') {
  5120. var href = target.getAttribute('href');
  5121. href && target.setAttribute('href', 'javascript:void(0)');
  5122. href && target.setAttribute('data-xs-href', href);
  5123. }
  5124. },
  5125. _triggerClick: function (e) {
  5126. var target = e.target;
  5127. if (!/(SELECT|INPUT|TEXTAREA)/i.test(target.tagName)) {
  5128. var ev = document.createEvent('MouseEvents');
  5129. ev.initMouseEvent('click', true, true, e.view, 1, target.screenX, target.screenY, target.clientX, target.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null);
  5130. target.dispatchEvent(ev);
  5131. }
  5132. }
  5133. });
  5134. if (typeof module == 'object' && module.exports) {
  5135. exports = SimuScroll;
  5136. } /** ignored by jsdoc **/ else {
  5137. return SimuScroll;
  5138. }
  5139. return exports;
  5140. }(simulate_scroll);
  5141. origin_scroll = function (exports) {
  5142. var Util = util, Base = base, Core = core, Animate = animate;
  5143. var transformOrigin = Util.prefixStyle('transformOrigin');
  5144. /**
  5145. * @constructor
  5146. * @param {object} cfg config for scroll
  5147. * @extends XScroll
  5148. * @example
  5149. * var xscroll = new OriginScroll({
  5150. * renderTo:"#scroll"
  5151. * });
  5152. * xscroll.render();
  5153. */
  5154. function OriginScroll(cfg) {
  5155. OriginScroll.superclass.constructor.call(this, cfg);
  5156. }
  5157. Util.extend(OriginScroll, Core, {
  5158. init: function () {
  5159. var self = this;
  5160. OriginScroll.superclass.init.call(this);
  5161. self.resetSize();
  5162. },
  5163. /**
  5164. * get scroll top value
  5165. * @memberof OriginScroll
  5166. * @return {number} scrollTop
  5167. */
  5168. getScrollTop: function () {
  5169. return this.renderTo.scrollTop;
  5170. },
  5171. /**
  5172. * get scroll left value
  5173. * @memberof OriginScroll
  5174. * @return {number} scrollLeft
  5175. */
  5176. getScrollLeft: function () {
  5177. return this.renderTo.scrollLeft;
  5178. },
  5179. /**
  5180. * vertical scroll absolute to the destination
  5181. * @memberof SimuScroll
  5182. * @param scrollTop {number} scrollTop
  5183. * @param duration {number} duration for animte
  5184. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  5185. **/
  5186. scrollTop: function (y, duration, easing, callback) {
  5187. var self = this;
  5188. var y = Math.round(y);
  5189. if (self.userConfig.lockY)
  5190. return;
  5191. var duration = duration || 0;
  5192. var easing = easing || 'quadratic';
  5193. var config = {
  5194. css: { scrollTop: y },
  5195. duration: duration,
  5196. easing: easing,
  5197. run: function (e) {
  5198. //trigger scroll event
  5199. self.trigger('scroll', {
  5200. scrollTop: self.getScrollTop(),
  5201. scrollLeft: self.getScrollLeft()
  5202. });
  5203. },
  5204. useTransition: false,
  5205. //scrollTop
  5206. end: callback
  5207. };
  5208. self.__timers.y = self.__timers.y || new Animate(self.renderTo, config);
  5209. //run
  5210. self.__timers.y.stop();
  5211. self.__timers.y.reset(config);
  5212. self.__timers.y.run();
  5213. },
  5214. /**
  5215. * horizontal scroll absolute to the destination
  5216. * @memberof SimuScroll
  5217. * @param scrollLeft {number} scrollLeft
  5218. * @param duration {number} duration for animte
  5219. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  5220. **/
  5221. scrollLeft: function (x, duration, easing, callback) {
  5222. var self = this;
  5223. var x = Math.round(x);
  5224. if (self.userConfig.lockX)
  5225. return;
  5226. var duration = duration || 0;
  5227. var easing = easing || 'quadratic';
  5228. var config = {
  5229. css: { scrollLeft: x },
  5230. duration: duration,
  5231. easing: easing,
  5232. run: function (e) {
  5233. //trigger scroll event
  5234. self.trigger('scroll', {
  5235. scrollTop: self.getScrollTop(),
  5236. scrollLeft: self.getScrollLeft()
  5237. });
  5238. },
  5239. useTransition: false,
  5240. //scrollTop
  5241. end: callback
  5242. };
  5243. self.__timers.x = self.__timers.x || new Animate(self.renderTo, config);
  5244. //run
  5245. self.__timers.x.stop();
  5246. self.__timers.x.reset(config);
  5247. self.__timers.x.run();
  5248. },
  5249. _bindEvt: function () {
  5250. OriginScroll.superclass._bindEvt.call(this);
  5251. var self = this;
  5252. if (self.__isEvtBind)
  5253. return;
  5254. self.__isEvtBind = true;
  5255. self.renderTo.addEventListener('scroll', function (e) {
  5256. self.trigger('scroll', {
  5257. type: 'scroll',
  5258. scrollTop: self.getScrollTop(),
  5259. scrollLeft: self.getScrollLeft()
  5260. });
  5261. }, false);
  5262. }
  5263. });
  5264. if (typeof module == 'object' && module.exports) {
  5265. exports = OriginScroll;
  5266. } /** ignored by jsdoc **/ else {
  5267. return OriginScroll;
  5268. }
  5269. return exports;
  5270. }(origin_scroll);
  5271. xscroll = function (exports) {
  5272. var Util = util, Base = base, Timer = timer, Animate = animate, Hammer = hammer, SimuScroll = simulate_scroll, OriginScroll = origin_scroll;
  5273. var XScroll = function (cfg) {
  5274. var _ = cfg && cfg.useOriginScroll ? OriginScroll : SimuScroll;
  5275. return new _(cfg);
  5276. };
  5277. /**
  5278. * Util
  5279. * @namespace Util
  5280. * @type {Object}
  5281. */
  5282. XScroll.Util = Util;
  5283. /**
  5284. * Base
  5285. * @namespace Base
  5286. * @type {Base}
  5287. */
  5288. XScroll.Base = Base;
  5289. /**
  5290. * Timer
  5291. * @namespace Timer
  5292. * @type {Timer}
  5293. */
  5294. XScroll.Timer = Timer;
  5295. /**
  5296. * Animate
  5297. * @namespace Animate
  5298. * @type {Animate}
  5299. */
  5300. XScroll.Animate = Animate;
  5301. /**
  5302. * Hammer
  5303. * @namespace Hammer
  5304. * @type {Hammer}
  5305. */
  5306. XScroll.Hammer = Hammer;
  5307. /**
  5308. * plugins
  5309. * @namespace Plugins
  5310. * @type {Object}
  5311. */
  5312. XScroll.Plugins = {};
  5313. if (typeof module == 'object' && module.exports) {
  5314. exports = XScroll;
  5315. } /** ignored by jsdoc **/ else {
  5316. return window.XScroll = XScroll;
  5317. }
  5318. return exports;
  5319. }(xscroll);
  5320. }());