material.js 142 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996
  1. ;(function() {
  2. "use strict";
  3. /**
  4. * @license
  5. * Copyright 2015 Google Inc. All Rights Reserved.
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. */
  19. /**
  20. * A component handler interface using the revealing module design pattern.
  21. * More details on this design pattern here:
  22. * https://github.com/jasonmayes/mdl-component-design-pattern
  23. *
  24. * @author Jason Mayes.
  25. */
  26. /* exported componentHandler */
  27. // Pre-defining the componentHandler interface, for closure documentation and
  28. // static verification.
  29. var componentHandler = {
  30. /**
  31. * Searches existing DOM for elements of our component type and upgrades them
  32. * if they have not already been upgraded.
  33. *
  34. * @param {string=} optJsClass the programatic name of the element class we
  35. * need to create a new instance of.
  36. * @param {string=} optCssClass the name of the CSS class elements of this
  37. * type will have.
  38. */
  39. upgradeDom: function(optJsClass, optCssClass) {},
  40. /**
  41. * Upgrades a specific element rather than all in the DOM.
  42. *
  43. * @param {!Element} element The element we wish to upgrade.
  44. * @param {string=} optJsClass Optional name of the class we want to upgrade
  45. * the element to.
  46. */
  47. upgradeElement: function(element, optJsClass) {},
  48. /**
  49. * Upgrades a specific list of elements rather than all in the DOM.
  50. *
  51. * @param {!Element|!Array<!Element>|!NodeList|!HTMLCollection} elements
  52. * The elements we wish to upgrade.
  53. */
  54. upgradeElements: function(elements) {},
  55. /**
  56. * Upgrades all registered components found in the current DOM. This is
  57. * automatically called on window load.
  58. */
  59. upgradeAllRegistered: function() {},
  60. /**
  61. * Allows user to be alerted to any upgrades that are performed for a given
  62. * component type
  63. *
  64. * @param {string} jsClass The class name of the MDL component we wish
  65. * to hook into for any upgrades performed.
  66. * @param {function(!HTMLElement)} callback The function to call upon an
  67. * upgrade. This function should expect 1 parameter - the HTMLElement which
  68. * got upgraded.
  69. */
  70. registerUpgradedCallback: function(jsClass, callback) {},
  71. /**
  72. * Registers a class for future use and attempts to upgrade existing DOM.
  73. *
  74. * @param {componentHandler.ComponentConfigPublic} config the registration configuration
  75. */
  76. register: function(config) {},
  77. /**
  78. * Downgrade either a given node, an array of nodes, or a NodeList.
  79. *
  80. * @param {!Node|!Array<!Node>|!NodeList} nodes
  81. */
  82. downgradeElements: function(nodes) {}
  83. };
  84. componentHandler = (function() {
  85. 'use strict';
  86. /** @type {!Array<componentHandler.ComponentConfig>} */
  87. var registeredComponents_ = [];
  88. /** @type {!Array<componentHandler.Component>} */
  89. var createdComponents_ = [];
  90. var componentConfigProperty_ = 'mdlComponentConfigInternal_';
  91. /**
  92. * Searches registered components for a class we are interested in using.
  93. * Optionally replaces a match with passed object if specified.
  94. *
  95. * @param {string} name The name of a class we want to use.
  96. * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.
  97. * @return {!Object|boolean}
  98. * @private
  99. */
  100. function findRegisteredClass_(name, optReplace) {
  101. for (var i = 0; i < registeredComponents_.length; i++) {
  102. if (registeredComponents_[i].className === name) {
  103. if (typeof optReplace !== 'undefined') {
  104. registeredComponents_[i] = optReplace;
  105. }
  106. return registeredComponents_[i];
  107. }
  108. }
  109. return false;
  110. }
  111. /**
  112. * Returns an array of the classNames of the upgraded classes on the element.
  113. *
  114. * @param {!Element} element The element to fetch data from.
  115. * @return {!Array<string>}
  116. * @private
  117. */
  118. function getUpgradedListOfElement_(element) {
  119. var dataUpgraded = element.getAttribute('data-upgraded');
  120. // Use `['']` as default value to conform the `,name,name...` style.
  121. return dataUpgraded === null ? [''] : dataUpgraded.split(',');
  122. }
  123. /**
  124. * Returns true if the given element has already been upgraded for the given
  125. * class.
  126. *
  127. * @param {!Element} element The element we want to check.
  128. * @param {string} jsClass The class to check for.
  129. * @returns {boolean}
  130. * @private
  131. */
  132. function isElementUpgraded_(element, jsClass) {
  133. var upgradedList = getUpgradedListOfElement_(element);
  134. return upgradedList.indexOf(jsClass) !== -1;
  135. }
  136. /**
  137. * Create an event object.
  138. *
  139. * @param {string} eventType The type name of the event.
  140. * @param {boolean} bubbles Whether the event should bubble up the DOM.
  141. * @param {boolean} cancelable Whether the event can be canceled.
  142. * @returns {!Event}
  143. */
  144. function createEvent_(eventType, bubbles, cancelable) {
  145. if ('CustomEvent' in window && typeof window.CustomEvent === 'function') {
  146. return new CustomEvent(eventType, {
  147. bubbles: bubbles,
  148. cancelable: cancelable
  149. });
  150. } else {
  151. var ev = document.createEvent('Events');
  152. ev.initEvent(eventType, bubbles, cancelable);
  153. return ev;
  154. }
  155. }
  156. /**
  157. * Searches existing DOM for elements of our component type and upgrades them
  158. * if they have not already been upgraded.
  159. *
  160. * @param {string=} optJsClass the programatic name of the element class we
  161. * need to create a new instance of.
  162. * @param {string=} optCssClass the name of the CSS class elements of this
  163. * type will have.
  164. */
  165. function upgradeDomInternal(optJsClass, optCssClass) {
  166. if (typeof optJsClass === 'undefined' &&
  167. typeof optCssClass === 'undefined') {
  168. for (var i = 0; i < registeredComponents_.length; i++) {
  169. upgradeDomInternal(registeredComponents_[i].className,
  170. registeredComponents_[i].cssClass);
  171. }
  172. } else {
  173. var jsClass = /** @type {string} */ (optJsClass);
  174. if (typeof optCssClass === 'undefined') {
  175. var registeredClass = findRegisteredClass_(jsClass);
  176. if (registeredClass) {
  177. optCssClass = registeredClass.cssClass;
  178. }
  179. }
  180. var elements = document.querySelectorAll('.' + optCssClass);
  181. for (var n = 0; n < elements.length; n++) {
  182. upgradeElementInternal(elements[n], jsClass);
  183. }
  184. }
  185. }
  186. /**
  187. * Upgrades a specific element rather than all in the DOM.
  188. *
  189. * @param {!Element} element The element we wish to upgrade.
  190. * @param {string=} optJsClass Optional name of the class we want to upgrade
  191. * the element to.
  192. */
  193. function upgradeElementInternal(element, optJsClass) {
  194. // Verify argument type.
  195. if (!(typeof element === 'object' && element instanceof Element)) {
  196. throw new Error('Invalid argument provided to upgrade MDL element.');
  197. }
  198. // Allow upgrade to be canceled by canceling emitted event.
  199. var upgradingEv = createEvent_('mdl-componentupgrading', true, true);
  200. element.dispatchEvent(upgradingEv);
  201. if (upgradingEv.defaultPrevented) {
  202. return;
  203. }
  204. var upgradedList = getUpgradedListOfElement_(element);
  205. var classesToUpgrade = [];
  206. // If jsClass is not provided scan the registered components to find the
  207. // ones matching the element's CSS classList.
  208. if (!optJsClass) {
  209. var classList = element.classList;
  210. registeredComponents_.forEach(function(component) {
  211. // Match CSS & Not to be upgraded & Not upgraded.
  212. if (classList.contains(component.cssClass) &&
  213. classesToUpgrade.indexOf(component) === -1 &&
  214. !isElementUpgraded_(element, component.className)) {
  215. classesToUpgrade.push(component);
  216. }
  217. });
  218. } else if (!isElementUpgraded_(element, optJsClass)) {
  219. classesToUpgrade.push(findRegisteredClass_(optJsClass));
  220. }
  221. // Upgrade the element for each classes.
  222. for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {
  223. registeredClass = classesToUpgrade[i];
  224. if (registeredClass) {
  225. // Mark element as upgraded.
  226. upgradedList.push(registeredClass.className);
  227. element.setAttribute('data-upgraded', upgradedList.join(','));
  228. var instance = new registeredClass.classConstructor(element);
  229. instance[componentConfigProperty_] = registeredClass;
  230. createdComponents_.push(instance);
  231. // Call any callbacks the user has registered with this component type.
  232. for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {
  233. registeredClass.callbacks[j](element);
  234. }
  235. if (registeredClass.widget) {
  236. // Assign per element instance for control over API
  237. element[registeredClass.className] = instance;
  238. }
  239. } else {
  240. throw new Error(
  241. 'Unable to find a registered component for the given class.');
  242. }
  243. var upgradedEv = createEvent_('mdl-componentupgraded', true, false);
  244. element.dispatchEvent(upgradedEv);
  245. }
  246. }
  247. /**
  248. * Upgrades a specific list of elements rather than all in the DOM.
  249. *
  250. * @param {!Element|!Array<!Element>|!NodeList|!HTMLCollection} elements
  251. * The elements we wish to upgrade.
  252. */
  253. function upgradeElementsInternal(elements) {
  254. if (!Array.isArray(elements)) {
  255. if (elements instanceof Element) {
  256. elements = [elements];
  257. } else {
  258. elements = Array.prototype.slice.call(elements);
  259. }
  260. }
  261. for (var i = 0, n = elements.length, element; i < n; i++) {
  262. element = elements[i];
  263. if (element instanceof HTMLElement) {
  264. upgradeElementInternal(element);
  265. if (element.children.length > 0) {
  266. upgradeElementsInternal(element.children);
  267. }
  268. }
  269. }
  270. }
  271. /**
  272. * Registers a class for future use and attempts to upgrade existing DOM.
  273. *
  274. * @param {componentHandler.ComponentConfigPublic} config
  275. */
  276. function registerInternal(config) {
  277. // In order to support both Closure-compiled and uncompiled code accessing
  278. // this method, we need to allow for both the dot and array syntax for
  279. // property access. You'll therefore see the `foo.bar || foo['bar']`
  280. // pattern repeated across this method.
  281. var widgetMissing = (typeof config.widget === 'undefined' &&
  282. typeof config['widget'] === 'undefined');
  283. var widget = true;
  284. if (!widgetMissing) {
  285. widget = config.widget || config['widget'];
  286. }
  287. var newConfig = /** @type {componentHandler.ComponentConfig} */ ({
  288. classConstructor: config.constructor || config['constructor'],
  289. className: config.classAsString || config['classAsString'],
  290. cssClass: config.cssClass || config['cssClass'],
  291. widget: widget,
  292. callbacks: []
  293. });
  294. registeredComponents_.forEach(function(item) {
  295. if (item.cssClass === newConfig.cssClass) {
  296. throw new Error('The provided cssClass has already been registered: ' + item.cssClass);
  297. }
  298. if (item.className === newConfig.className) {
  299. throw new Error('The provided className has already been registered');
  300. }
  301. });
  302. if (config.constructor.prototype
  303. .hasOwnProperty(componentConfigProperty_)) {
  304. throw new Error(
  305. 'MDL component classes must not have ' + componentConfigProperty_ +
  306. ' defined as a property.');
  307. }
  308. var found = findRegisteredClass_(config.classAsString, newConfig);
  309. if (!found) {
  310. registeredComponents_.push(newConfig);
  311. }
  312. }
  313. /**
  314. * Allows user to be alerted to any upgrades that are performed for a given
  315. * component type
  316. *
  317. * @param {string} jsClass The class name of the MDL component we wish
  318. * to hook into for any upgrades performed.
  319. * @param {function(!HTMLElement)} callback The function to call upon an
  320. * upgrade. This function should expect 1 parameter - the HTMLElement which
  321. * got upgraded.
  322. */
  323. function registerUpgradedCallbackInternal(jsClass, callback) {
  324. var regClass = findRegisteredClass_(jsClass);
  325. if (regClass) {
  326. regClass.callbacks.push(callback);
  327. }
  328. }
  329. /**
  330. * Upgrades all registered components found in the current DOM. This is
  331. * automatically called on window load.
  332. */
  333. function upgradeAllRegisteredInternal() {
  334. for (var n = 0; n < registeredComponents_.length; n++) {
  335. upgradeDomInternal(registeredComponents_[n].className);
  336. }
  337. }
  338. /**
  339. * Check the component for the downgrade method.
  340. * Execute if found.
  341. * Remove component from createdComponents list.
  342. *
  343. * @param {?componentHandler.Component} component
  344. */
  345. function deconstructComponentInternal(component) {
  346. if (component) {
  347. var componentIndex = createdComponents_.indexOf(component);
  348. createdComponents_.splice(componentIndex, 1);
  349. var upgrades = component.element_.getAttribute('data-upgraded').split(',');
  350. var componentPlace = upgrades.indexOf(component[componentConfigProperty_].classAsString);
  351. upgrades.splice(componentPlace, 1);
  352. component.element_.setAttribute('data-upgraded', upgrades.join(','));
  353. var ev = createEvent_('mdl-componentdowngraded', true, false);
  354. component.element_.dispatchEvent(ev);
  355. }
  356. }
  357. /**
  358. * Downgrade either a given node, an array of nodes, or a NodeList.
  359. *
  360. * @param {!Node|!Array<!Node>|!NodeList} nodes
  361. */
  362. function downgradeNodesInternal(nodes) {
  363. /**
  364. * Auxiliary function to downgrade a single node.
  365. * @param {!Node} node the node to be downgraded
  366. */
  367. var downgradeNode = function(node) {
  368. createdComponents_.filter(function(item) {
  369. return item.element_ === node;
  370. }).forEach(deconstructComponentInternal);
  371. };
  372. if (nodes instanceof Array || nodes instanceof NodeList) {
  373. for (var n = 0; n < nodes.length; n++) {
  374. downgradeNode(nodes[n]);
  375. }
  376. } else if (nodes instanceof Node) {
  377. downgradeNode(nodes);
  378. } else {
  379. throw new Error('Invalid argument provided to downgrade MDL nodes.');
  380. }
  381. }
  382. // Now return the functions that should be made public with their publicly
  383. // facing names...
  384. return {
  385. upgradeDom: upgradeDomInternal,
  386. upgradeElement: upgradeElementInternal,
  387. upgradeElements: upgradeElementsInternal,
  388. upgradeAllRegistered: upgradeAllRegisteredInternal,
  389. registerUpgradedCallback: registerUpgradedCallbackInternal,
  390. register: registerInternal,
  391. downgradeElements: downgradeNodesInternal
  392. };
  393. })();
  394. /**
  395. * Describes the type of a registered component type managed by
  396. * componentHandler. Provided for benefit of the Closure compiler.
  397. *
  398. * @typedef {{
  399. * constructor: Function,
  400. * classAsString: string,
  401. * cssClass: string,
  402. * widget: (string|boolean|undefined)
  403. * }}
  404. */
  405. componentHandler.ComponentConfigPublic; // jshint ignore:line
  406. /**
  407. * Describes the type of a registered component type managed by
  408. * componentHandler. Provided for benefit of the Closure compiler.
  409. *
  410. * @typedef {{
  411. * constructor: !Function,
  412. * className: string,
  413. * cssClass: string,
  414. * widget: (string|boolean),
  415. * callbacks: !Array<function(!HTMLElement)>
  416. * }}
  417. */
  418. componentHandler.ComponentConfig; // jshint ignore:line
  419. /**
  420. * Created component (i.e., upgraded element) type as managed by
  421. * componentHandler. Provided for benefit of the Closure compiler.
  422. *
  423. * @typedef {{
  424. * element_: !HTMLElement,
  425. * className: string,
  426. * classAsString: string,
  427. * cssClass: string,
  428. * widget: string
  429. * }}
  430. */
  431. componentHandler.Component; // jshint ignore:line
  432. // Export all symbols, for the benefit of Closure compiler.
  433. // No effect on uncompiled code.
  434. componentHandler['upgradeDom'] = componentHandler.upgradeDom;
  435. componentHandler['upgradeElement'] = componentHandler.upgradeElement;
  436. componentHandler['upgradeElements'] = componentHandler.upgradeElements;
  437. componentHandler['upgradeAllRegistered'] =
  438. componentHandler.upgradeAllRegistered;
  439. componentHandler['registerUpgradedCallback'] =
  440. componentHandler.registerUpgradedCallback;
  441. componentHandler['register'] = componentHandler.register;
  442. componentHandler['downgradeElements'] = componentHandler.downgradeElements;
  443. window.componentHandler = componentHandler;
  444. window['componentHandler'] = componentHandler;
  445. window.addEventListener('load', function() {
  446. 'use strict';
  447. /**
  448. * Performs a "Cutting the mustard" test. If the browser supports the features
  449. * tested, adds a mdl-js class to the <html> element. It then upgrades all MDL
  450. * components requiring JavaScript.
  451. */
  452. if ('classList' in document.createElement('div') &&
  453. 'querySelector' in document &&
  454. 'addEventListener' in window && Array.prototype.forEach) {
  455. document.documentElement.classList.add('mdl-js');
  456. componentHandler.upgradeAllRegistered();
  457. } else {
  458. /**
  459. * Dummy function to avoid JS errors.
  460. */
  461. componentHandler.upgradeElement = function() {};
  462. /**
  463. * Dummy function to avoid JS errors.
  464. */
  465. componentHandler.register = function() {};
  466. }
  467. });
  468. // Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js
  469. // Adapted from https://gist.github.com/paulirish/1579671 which derived from
  470. // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  471. // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
  472. // requestAnimationFrame polyfill by Erik Möller.
  473. // Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon
  474. // MIT license
  475. if (!Date.now) {
  476. /**
  477. * Date.now polyfill.
  478. * @return {number} the current Date
  479. */
  480. Date.now = function () {
  481. return new Date().getTime();
  482. };
  483. Date['now'] = Date.now;
  484. }
  485. var vendors = [
  486. 'webkit',
  487. 'moz'
  488. ];
  489. for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
  490. var vp = vendors[i];
  491. window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];
  492. window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];
  493. window['requestAnimationFrame'] = window.requestAnimationFrame;
  494. window['cancelAnimationFrame'] = window.cancelAnimationFrame;
  495. }
  496. if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
  497. var lastTime = 0;
  498. /**
  499. * requestAnimationFrame polyfill.
  500. * @param {!Function} callback the callback function.
  501. */
  502. window.requestAnimationFrame = function (callback) {
  503. var now = Date.now();
  504. var nextTime = Math.max(lastTime + 16, now);
  505. return setTimeout(function () {
  506. callback(lastTime = nextTime);
  507. }, nextTime - now);
  508. };
  509. window.cancelAnimationFrame = clearTimeout;
  510. window['requestAnimationFrame'] = window.requestAnimationFrame;
  511. window['cancelAnimationFrame'] = window.cancelAnimationFrame;
  512. }
  513. /**
  514. * @license
  515. * Copyright 2015 Google Inc. All Rights Reserved.
  516. *
  517. * Licensed under the Apache License, Version 2.0 (the "License");
  518. * you may not use this file except in compliance with the License.
  519. * You may obtain a copy of the License at
  520. *
  521. * http://www.apache.org/licenses/LICENSE-2.0
  522. *
  523. * Unless required by applicable law or agreed to in writing, software
  524. * distributed under the License is distributed on an "AS IS" BASIS,
  525. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  526. * See the License for the specific language governing permissions and
  527. * limitations under the License.
  528. */
  529. /**
  530. * Class constructor for Button MDL component.
  531. * Implements MDL component design pattern defined at:
  532. * https://github.com/jasonmayes/mdl-component-design-pattern
  533. *
  534. * @param {HTMLElement} element The element that will be upgraded.
  535. */
  536. var MaterialButton = function MaterialButton(element) {
  537. this.element_ = element;
  538. // Initialize instance.
  539. this.init();
  540. };
  541. window['MaterialButton'] = MaterialButton;
  542. /**
  543. * Store constants in one place so they can be updated easily.
  544. *
  545. * @enum {string | number}
  546. * @private
  547. */
  548. MaterialButton.prototype.Constant_ = {};
  549. /**
  550. * Store strings for class names defined by this component that are used in
  551. * JavaScript. This allows us to simply change it in one place should we
  552. * decide to modify at a later date.
  553. *
  554. * @enum {string}
  555. * @private
  556. */
  557. MaterialButton.prototype.CssClasses_ = {
  558. RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  559. RIPPLE_CONTAINER: 'mdl-button__ripple-container',
  560. RIPPLE: 'mdl-ripple'
  561. };
  562. /**
  563. * Handle blur of element.
  564. *
  565. * @param {Event} event The event that fired.
  566. * @private
  567. */
  568. MaterialButton.prototype.blurHandler_ = function (event) {
  569. if (event) {
  570. this.element_.blur();
  571. }
  572. };
  573. // Public methods.
  574. /**
  575. * Disable button.
  576. *
  577. * @public
  578. */
  579. MaterialButton.prototype.disable = function () {
  580. this.element_.disabled = true;
  581. };
  582. MaterialButton.prototype['disable'] = MaterialButton.prototype.disable;
  583. /**
  584. * Enable button.
  585. *
  586. * @public
  587. */
  588. MaterialButton.prototype.enable = function () {
  589. this.element_.disabled = false;
  590. };
  591. MaterialButton.prototype['enable'] = MaterialButton.prototype.enable;
  592. /**
  593. * Initialize element.
  594. */
  595. MaterialButton.prototype.init = function () {
  596. if (this.element_) {
  597. if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  598. var rippleContainer = document.createElement('span');
  599. rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  600. this.rippleElement_ = document.createElement('span');
  601. this.rippleElement_.classList.add(this.CssClasses_.RIPPLE);
  602. rippleContainer.appendChild(this.rippleElement_);
  603. this.boundRippleBlurHandler = this.blurHandler_.bind(this);
  604. this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler);
  605. this.element_.appendChild(rippleContainer);
  606. }
  607. this.boundButtonBlurHandler = this.blurHandler_.bind(this);
  608. this.element_.addEventListener('mouseup', this.boundButtonBlurHandler);
  609. this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler);
  610. }
  611. };
  612. // The component registers itself. It can assume componentHandler is available
  613. // in the global scope.
  614. componentHandler.register({
  615. constructor: MaterialButton,
  616. classAsString: 'MaterialButton',
  617. cssClass: 'mdl-js-button',
  618. widget: true
  619. });
  620. /**
  621. * @license
  622. * Copyright 2015 Google Inc. All Rights Reserved.
  623. *
  624. * Licensed under the Apache License, Version 2.0 (the "License");
  625. * you may not use this file except in compliance with the License.
  626. * You may obtain a copy of the License at
  627. *
  628. * http://www.apache.org/licenses/LICENSE-2.0
  629. *
  630. * Unless required by applicable law or agreed to in writing, software
  631. * distributed under the License is distributed on an "AS IS" BASIS,
  632. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  633. * See the License for the specific language governing permissions and
  634. * limitations under the License.
  635. */
  636. /**
  637. * Class constructor for Checkbox MDL component.
  638. * Implements MDL component design pattern defined at:
  639. * https://github.com/jasonmayes/mdl-component-design-pattern
  640. *
  641. * @constructor
  642. * @param {HTMLElement} element The element that will be upgraded.
  643. */
  644. var MaterialCheckbox = function MaterialCheckbox(element) {
  645. this.element_ = element;
  646. // Initialize instance.
  647. this.init();
  648. };
  649. window['MaterialCheckbox'] = MaterialCheckbox;
  650. /**
  651. * Store constants in one place so they can be updated easily.
  652. *
  653. * @enum {string | number}
  654. * @private
  655. */
  656. MaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
  657. /**
  658. * Store strings for class names defined by this component that are used in
  659. * JavaScript. This allows us to simply change it in one place should we
  660. * decide to modify at a later date.
  661. *
  662. * @enum {string}
  663. * @private
  664. */
  665. MaterialCheckbox.prototype.CssClasses_ = {
  666. INPUT: 'mdl-checkbox__input',
  667. BOX_OUTLINE: 'mdl-checkbox__box-outline',
  668. FOCUS_HELPER: 'mdl-checkbox__focus-helper',
  669. TICK_OUTLINE: 'mdl-checkbox__tick-outline',
  670. RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  671. RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  672. RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container',
  673. RIPPLE_CENTER: 'mdl-ripple--center',
  674. RIPPLE: 'mdl-ripple',
  675. IS_FOCUSED: 'is-focused',
  676. IS_DISABLED: 'is-disabled',
  677. IS_CHECKED: 'is-checked',
  678. IS_UPGRADED: 'is-upgraded'
  679. };
  680. /**
  681. * Handle change of state.
  682. *
  683. * @param {Event} event The event that fired.
  684. * @private
  685. */
  686. MaterialCheckbox.prototype.onChange_ = function (event) {
  687. this.updateClasses_();
  688. };
  689. /**
  690. * Handle focus of element.
  691. *
  692. * @param {Event} event The event that fired.
  693. * @private
  694. */
  695. MaterialCheckbox.prototype.onFocus_ = function (event) {
  696. this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  697. };
  698. /**
  699. * Handle lost focus of element.
  700. *
  701. * @param {Event} event The event that fired.
  702. * @private
  703. */
  704. MaterialCheckbox.prototype.onBlur_ = function (event) {
  705. this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  706. };
  707. /**
  708. * Handle mouseup.
  709. *
  710. * @param {Event} event The event that fired.
  711. * @private
  712. */
  713. MaterialCheckbox.prototype.onMouseUp_ = function (event) {
  714. this.blur_();
  715. };
  716. /**
  717. * Handle class updates.
  718. *
  719. * @private
  720. */
  721. MaterialCheckbox.prototype.updateClasses_ = function () {
  722. this.checkDisabled();
  723. this.checkToggleState();
  724. };
  725. /**
  726. * Add blur.
  727. *
  728. * @private
  729. */
  730. MaterialCheckbox.prototype.blur_ = function () {
  731. // TODO: figure out why there's a focus event being fired after our blur,
  732. // so that we can avoid this hack.
  733. window.setTimeout(function () {
  734. this.inputElement_.blur();
  735. }.bind(this), this.Constant_.TINY_TIMEOUT);
  736. };
  737. // Public methods.
  738. /**
  739. * Check the inputs toggle state and update display.
  740. *
  741. * @public
  742. */
  743. MaterialCheckbox.prototype.checkToggleState = function () {
  744. if (this.inputElement_.checked) {
  745. this.element_.classList.add(this.CssClasses_.IS_CHECKED);
  746. } else {
  747. this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
  748. }
  749. };
  750. MaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState;
  751. /**
  752. * Check the inputs disabled state and update display.
  753. *
  754. * @public
  755. */
  756. MaterialCheckbox.prototype.checkDisabled = function () {
  757. if (this.inputElement_.disabled) {
  758. this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  759. } else {
  760. this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  761. }
  762. };
  763. MaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled;
  764. /**
  765. * Disable checkbox.
  766. *
  767. * @public
  768. */
  769. MaterialCheckbox.prototype.disable = function () {
  770. this.inputElement_.disabled = true;
  771. this.updateClasses_();
  772. };
  773. MaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable;
  774. /**
  775. * Enable checkbox.
  776. *
  777. * @public
  778. */
  779. MaterialCheckbox.prototype.enable = function () {
  780. this.inputElement_.disabled = false;
  781. this.updateClasses_();
  782. };
  783. MaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable;
  784. /**
  785. * Check checkbox.
  786. *
  787. * @public
  788. */
  789. MaterialCheckbox.prototype.check = function () {
  790. this.inputElement_.checked = true;
  791. this.updateClasses_();
  792. };
  793. MaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check;
  794. /**
  795. * Uncheck checkbox.
  796. *
  797. * @public
  798. */
  799. MaterialCheckbox.prototype.uncheck = function () {
  800. this.inputElement_.checked = false;
  801. this.updateClasses_();
  802. };
  803. MaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck;
  804. /**
  805. * Initialize element.
  806. */
  807. MaterialCheckbox.prototype.init = function () {
  808. if (this.element_) {
  809. this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
  810. var boxOutline = document.createElement('span');
  811. boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE);
  812. var tickContainer = document.createElement('span');
  813. tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER);
  814. var tickOutline = document.createElement('span');
  815. tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE);
  816. boxOutline.appendChild(tickOutline);
  817. this.element_.appendChild(tickContainer);
  818. this.element_.appendChild(boxOutline);
  819. if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  820. this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  821. this.rippleContainerElement_ = document.createElement('span');
  822. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  823. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);
  824. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);
  825. this.boundRippleMouseUp = this.onMouseUp_.bind(this);
  826. this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);
  827. var ripple = document.createElement('span');
  828. ripple.classList.add(this.CssClasses_.RIPPLE);
  829. this.rippleContainerElement_.appendChild(ripple);
  830. this.element_.appendChild(this.rippleContainerElement_);
  831. }
  832. this.boundInputOnChange = this.onChange_.bind(this);
  833. this.boundInputOnFocus = this.onFocus_.bind(this);
  834. this.boundInputOnBlur = this.onBlur_.bind(this);
  835. this.boundElementMouseUp = this.onMouseUp_.bind(this);
  836. this.inputElement_.addEventListener('change', this.boundInputOnChange);
  837. this.inputElement_.addEventListener('focus', this.boundInputOnFocus);
  838. this.inputElement_.addEventListener('blur', this.boundInputOnBlur);
  839. this.element_.addEventListener('mouseup', this.boundElementMouseUp);
  840. this.updateClasses_();
  841. this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  842. }
  843. };
  844. // The component registers itself. It can assume componentHandler is available
  845. // in the global scope.
  846. componentHandler.register({
  847. constructor: MaterialCheckbox,
  848. classAsString: 'MaterialCheckbox',
  849. cssClass: 'mdl-js-checkbox',
  850. widget: true
  851. });
  852. /**
  853. * @license
  854. * Copyright 2015 Google Inc. All Rights Reserved.
  855. *
  856. * Licensed under the Apache License, Version 2.0 (the "License");
  857. * you may not use this file except in compliance with the License.
  858. * You may obtain a copy of the License at
  859. *
  860. * http://www.apache.org/licenses/LICENSE-2.0
  861. *
  862. * Unless required by applicable law or agreed to in writing, software
  863. * distributed under the License is distributed on an "AS IS" BASIS,
  864. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  865. * See the License for the specific language governing permissions and
  866. * limitations under the License.
  867. */
  868. /**
  869. * Class constructor for icon toggle MDL component.
  870. * Implements MDL component design pattern defined at:
  871. * https://github.com/jasonmayes/mdl-component-design-pattern
  872. *
  873. * @constructor
  874. * @param {HTMLElement} element The element that will be upgraded.
  875. */
  876. var MaterialIconToggle = function MaterialIconToggle(element) {
  877. this.element_ = element;
  878. // Initialize instance.
  879. this.init();
  880. };
  881. window['MaterialIconToggle'] = MaterialIconToggle;
  882. /**
  883. * Store constants in one place so they can be updated easily.
  884. *
  885. * @enum {string | number}
  886. * @private
  887. */
  888. MaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
  889. /**
  890. * Store strings for class names defined by this component that are used in
  891. * JavaScript. This allows us to simply change it in one place should we
  892. * decide to modify at a later date.
  893. *
  894. * @enum {string}
  895. * @private
  896. */
  897. MaterialIconToggle.prototype.CssClasses_ = {
  898. INPUT: 'mdl-icon-toggle__input',
  899. JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  900. RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  901. RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container',
  902. RIPPLE_CENTER: 'mdl-ripple--center',
  903. RIPPLE: 'mdl-ripple',
  904. IS_FOCUSED: 'is-focused',
  905. IS_DISABLED: 'is-disabled',
  906. IS_CHECKED: 'is-checked'
  907. };
  908. /**
  909. * Handle change of state.
  910. *
  911. * @param {Event} event The event that fired.
  912. * @private
  913. */
  914. MaterialIconToggle.prototype.onChange_ = function (event) {
  915. this.updateClasses_();
  916. };
  917. /**
  918. * Handle focus of element.
  919. *
  920. * @param {Event} event The event that fired.
  921. * @private
  922. */
  923. MaterialIconToggle.prototype.onFocus_ = function (event) {
  924. this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  925. };
  926. /**
  927. * Handle lost focus of element.
  928. *
  929. * @param {Event} event The event that fired.
  930. * @private
  931. */
  932. MaterialIconToggle.prototype.onBlur_ = function (event) {
  933. this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  934. };
  935. /**
  936. * Handle mouseup.
  937. *
  938. * @param {Event} event The event that fired.
  939. * @private
  940. */
  941. MaterialIconToggle.prototype.onMouseUp_ = function (event) {
  942. this.blur_();
  943. };
  944. /**
  945. * Handle class updates.
  946. *
  947. * @private
  948. */
  949. MaterialIconToggle.prototype.updateClasses_ = function () {
  950. this.checkDisabled();
  951. this.checkToggleState();
  952. };
  953. /**
  954. * Add blur.
  955. *
  956. * @private
  957. */
  958. MaterialIconToggle.prototype.blur_ = function () {
  959. // TODO: figure out why there's a focus event being fired after our blur,
  960. // so that we can avoid this hack.
  961. window.setTimeout(function () {
  962. this.inputElement_.blur();
  963. }.bind(this), this.Constant_.TINY_TIMEOUT);
  964. };
  965. // Public methods.
  966. /**
  967. * Check the inputs toggle state and update display.
  968. *
  969. * @public
  970. */
  971. MaterialIconToggle.prototype.checkToggleState = function () {
  972. if (this.inputElement_.checked) {
  973. this.element_.classList.add(this.CssClasses_.IS_CHECKED);
  974. } else {
  975. this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
  976. }
  977. };
  978. MaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState;
  979. /**
  980. * Check the inputs disabled state and update display.
  981. *
  982. * @public
  983. */
  984. MaterialIconToggle.prototype.checkDisabled = function () {
  985. if (this.inputElement_.disabled) {
  986. this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  987. } else {
  988. this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  989. }
  990. };
  991. MaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled;
  992. /**
  993. * Disable icon toggle.
  994. *
  995. * @public
  996. */
  997. MaterialIconToggle.prototype.disable = function () {
  998. this.inputElement_.disabled = true;
  999. this.updateClasses_();
  1000. };
  1001. MaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable;
  1002. /**
  1003. * Enable icon toggle.
  1004. *
  1005. * @public
  1006. */
  1007. MaterialIconToggle.prototype.enable = function () {
  1008. this.inputElement_.disabled = false;
  1009. this.updateClasses_();
  1010. };
  1011. MaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable;
  1012. /**
  1013. * Check icon toggle.
  1014. *
  1015. * @public
  1016. */
  1017. MaterialIconToggle.prototype.check = function () {
  1018. this.inputElement_.checked = true;
  1019. this.updateClasses_();
  1020. };
  1021. MaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check;
  1022. /**
  1023. * Uncheck icon toggle.
  1024. *
  1025. * @public
  1026. */
  1027. MaterialIconToggle.prototype.uncheck = function () {
  1028. this.inputElement_.checked = false;
  1029. this.updateClasses_();
  1030. };
  1031. MaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck;
  1032. /**
  1033. * Initialize element.
  1034. */
  1035. MaterialIconToggle.prototype.init = function () {
  1036. if (this.element_) {
  1037. this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
  1038. if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {
  1039. this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  1040. this.rippleContainerElement_ = document.createElement('span');
  1041. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  1042. this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT);
  1043. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);
  1044. this.boundRippleMouseUp = this.onMouseUp_.bind(this);
  1045. this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);
  1046. var ripple = document.createElement('span');
  1047. ripple.classList.add(this.CssClasses_.RIPPLE);
  1048. this.rippleContainerElement_.appendChild(ripple);
  1049. this.element_.appendChild(this.rippleContainerElement_);
  1050. }
  1051. this.boundInputOnChange = this.onChange_.bind(this);
  1052. this.boundInputOnFocus = this.onFocus_.bind(this);
  1053. this.boundInputOnBlur = this.onBlur_.bind(this);
  1054. this.boundElementOnMouseUp = this.onMouseUp_.bind(this);
  1055. this.inputElement_.addEventListener('change', this.boundInputOnChange);
  1056. this.inputElement_.addEventListener('focus', this.boundInputOnFocus);
  1057. this.inputElement_.addEventListener('blur', this.boundInputOnBlur);
  1058. this.element_.addEventListener('mouseup', this.boundElementOnMouseUp);
  1059. this.updateClasses_();
  1060. this.element_.classList.add('is-upgraded');
  1061. }
  1062. };
  1063. // The component registers itself. It can assume componentHandler is available
  1064. // in the global scope.
  1065. componentHandler.register({
  1066. constructor: MaterialIconToggle,
  1067. classAsString: 'MaterialIconToggle',
  1068. cssClass: 'mdl-js-icon-toggle',
  1069. widget: true
  1070. });
  1071. /**
  1072. * @license
  1073. * Copyright 2015 Google Inc. All Rights Reserved.
  1074. *
  1075. * Licensed under the Apache License, Version 2.0 (the "License");
  1076. * you may not use this file except in compliance with the License.
  1077. * You may obtain a copy of the License at
  1078. *
  1079. * http://www.apache.org/licenses/LICENSE-2.0
  1080. *
  1081. * Unless required by applicable law or agreed to in writing, software
  1082. * distributed under the License is distributed on an "AS IS" BASIS,
  1083. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1084. * See the License for the specific language governing permissions and
  1085. * limitations under the License.
  1086. */
  1087. /**
  1088. * Class constructor for dropdown MDL component.
  1089. * Implements MDL component design pattern defined at:
  1090. * https://github.com/jasonmayes/mdl-component-design-pattern
  1091. *
  1092. * @constructor
  1093. * @param {HTMLElement} element The element that will be upgraded.
  1094. */
  1095. var MaterialMenu = function MaterialMenu(element) {
  1096. this.element_ = element;
  1097. // Initialize instance.
  1098. this.init();
  1099. };
  1100. window['MaterialMenu'] = MaterialMenu;
  1101. /**
  1102. * Store constants in one place so they can be updated easily.
  1103. *
  1104. * @enum {string | number}
  1105. * @private
  1106. */
  1107. MaterialMenu.prototype.Constant_ = {
  1108. // Total duration of the menu animation.
  1109. TRANSITION_DURATION_SECONDS: 0.3,
  1110. // The fraction of the total duration we want to use for menu item animations.
  1111. TRANSITION_DURATION_FRACTION: 0.8,
  1112. // How long the menu stays open after choosing an option (so the user can see
  1113. // the ripple).
  1114. CLOSE_TIMEOUT: 150
  1115. };
  1116. /**
  1117. * Keycodes, for code readability.
  1118. *
  1119. * @enum {number}
  1120. * @private
  1121. */
  1122. MaterialMenu.prototype.Keycodes_ = {
  1123. ENTER: 13,
  1124. ESCAPE: 27,
  1125. SPACE: 32,
  1126. UP_ARROW: 38,
  1127. DOWN_ARROW: 40
  1128. };
  1129. /**
  1130. * Store strings for class names defined by this component that are used in
  1131. * JavaScript. This allows us to simply change it in one place should we
  1132. * decide to modify at a later date.
  1133. *
  1134. * @enum {string}
  1135. * @private
  1136. */
  1137. MaterialMenu.prototype.CssClasses_ = {
  1138. CONTAINER: 'mdl-menu__container',
  1139. OUTLINE: 'mdl-menu__outline',
  1140. ITEM: 'mdl-menu__item',
  1141. ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',
  1142. RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  1143. RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  1144. RIPPLE: 'mdl-ripple',
  1145. // Statuses
  1146. IS_UPGRADED: 'is-upgraded',
  1147. IS_VISIBLE: 'is-visible',
  1148. IS_ANIMATING: 'is-animating',
  1149. // Alignment options
  1150. BOTTOM_LEFT: 'mdl-menu--bottom-left',
  1151. // This is the default.
  1152. BOTTOM_RIGHT: 'mdl-menu--bottom-right',
  1153. TOP_LEFT: 'mdl-menu--top-left',
  1154. TOP_RIGHT: 'mdl-menu--top-right',
  1155. UNALIGNED: 'mdl-menu--unaligned'
  1156. };
  1157. /**
  1158. * Initialize element.
  1159. */
  1160. MaterialMenu.prototype.init = function () {
  1161. if (this.element_) {
  1162. // Create container for the menu.
  1163. var container = document.createElement('div');
  1164. container.classList.add(this.CssClasses_.CONTAINER);
  1165. this.element_.parentElement.insertBefore(container, this.element_);
  1166. this.element_.parentElement.removeChild(this.element_);
  1167. container.appendChild(this.element_);
  1168. this.container_ = container;
  1169. // Create outline for the menu (shadow and background).
  1170. var outline = document.createElement('div');
  1171. outline.classList.add(this.CssClasses_.OUTLINE);
  1172. this.outline_ = outline;
  1173. container.insertBefore(outline, this.element_);
  1174. // Find the "for" element and bind events to it.
  1175. var forElId = this.element_.getAttribute('for') || this.element_.getAttribute('data-mdl-for');
  1176. var forEl = null;
  1177. if (forElId) {
  1178. forEl = document.getElementById(forElId);
  1179. if (forEl) {
  1180. this.forElement_ = forEl;
  1181. forEl.addEventListener('click', this.handleForClick_.bind(this));
  1182. forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this));
  1183. }
  1184. }
  1185. var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
  1186. this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this);
  1187. this.boundItemClick_ = this.handleItemClick_.bind(this);
  1188. for (var i = 0; i < items.length; i++) {
  1189. // Add a listener to each menu item.
  1190. items[i].addEventListener('click', this.boundItemClick_);
  1191. // Add a tab index to each menu item.
  1192. items[i].tabIndex = '-1';
  1193. // Add a keyboard listener to each menu item.
  1194. items[i].addEventListener('keydown', this.boundItemKeydown_);
  1195. }
  1196. // Add ripple classes to each item, if the user has enabled ripples.
  1197. if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  1198. this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  1199. for (i = 0; i < items.length; i++) {
  1200. var item = items[i];
  1201. var rippleContainer = document.createElement('span');
  1202. rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);
  1203. var ripple = document.createElement('span');
  1204. ripple.classList.add(this.CssClasses_.RIPPLE);
  1205. rippleContainer.appendChild(ripple);
  1206. item.appendChild(rippleContainer);
  1207. item.classList.add(this.CssClasses_.RIPPLE_EFFECT);
  1208. }
  1209. }
  1210. // Copy alignment classes to the container, so the outline can use them.
  1211. if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {
  1212. this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);
  1213. }
  1214. if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
  1215. this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);
  1216. }
  1217. if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
  1218. this.outline_.classList.add(this.CssClasses_.TOP_LEFT);
  1219. }
  1220. if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1221. this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);
  1222. }
  1223. if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
  1224. this.outline_.classList.add(this.CssClasses_.UNALIGNED);
  1225. }
  1226. container.classList.add(this.CssClasses_.IS_UPGRADED);
  1227. }
  1228. };
  1229. /**
  1230. * Handles a click on the "for" element, by positioning the menu and then
  1231. * toggling it.
  1232. *
  1233. * @param {Event} evt The event that fired.
  1234. * @private
  1235. */
  1236. MaterialMenu.prototype.handleForClick_ = function (evt) {
  1237. if (this.element_ && this.forElement_) {
  1238. var rect = this.forElement_.getBoundingClientRect();
  1239. var forRect = this.forElement_.parentElement.getBoundingClientRect();
  1240. if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
  1241. } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
  1242. // Position below the "for" element, aligned to its right.
  1243. this.container_.style.right = forRect.right - rect.right + 'px';
  1244. this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
  1245. } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
  1246. // Position above the "for" element, aligned to its left.
  1247. this.container_.style.left = this.forElement_.offsetLeft + 'px';
  1248. this.container_.style.bottom = forRect.bottom - rect.top + 'px';
  1249. } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1250. // Position above the "for" element, aligned to its right.
  1251. this.container_.style.right = forRect.right - rect.right + 'px';
  1252. this.container_.style.bottom = forRect.bottom - rect.top + 'px';
  1253. } else {
  1254. // Default: position below the "for" element, aligned to its left.
  1255. this.container_.style.left = this.forElement_.offsetLeft + 'px';
  1256. this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
  1257. }
  1258. }
  1259. this.toggle(evt);
  1260. };
  1261. /**
  1262. * Handles a keyboard event on the "for" element.
  1263. *
  1264. * @param {Event} evt The event that fired.
  1265. * @private
  1266. */
  1267. MaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) {
  1268. if (this.element_ && this.container_ && this.forElement_) {
  1269. var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');
  1270. if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
  1271. if (evt.keyCode === this.Keycodes_.UP_ARROW) {
  1272. evt.preventDefault();
  1273. items[items.length - 1].focus();
  1274. } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
  1275. evt.preventDefault();
  1276. items[0].focus();
  1277. }
  1278. }
  1279. }
  1280. };
  1281. /**
  1282. * Handles a keyboard event on an item.
  1283. *
  1284. * @param {Event} evt The event that fired.
  1285. * @private
  1286. */
  1287. MaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) {
  1288. if (this.element_ && this.container_) {
  1289. var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');
  1290. if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
  1291. var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);
  1292. if (evt.keyCode === this.Keycodes_.UP_ARROW) {
  1293. evt.preventDefault();
  1294. if (currentIndex > 0) {
  1295. items[currentIndex - 1].focus();
  1296. } else {
  1297. items[items.length - 1].focus();
  1298. }
  1299. } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
  1300. evt.preventDefault();
  1301. if (items.length > currentIndex + 1) {
  1302. items[currentIndex + 1].focus();
  1303. } else {
  1304. items[0].focus();
  1305. }
  1306. } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {
  1307. evt.preventDefault();
  1308. // Send mousedown and mouseup to trigger ripple.
  1309. var e = new MouseEvent('mousedown');
  1310. evt.target.dispatchEvent(e);
  1311. e = new MouseEvent('mouseup');
  1312. evt.target.dispatchEvent(e);
  1313. // Send click.
  1314. evt.target.click();
  1315. } else if (evt.keyCode === this.Keycodes_.ESCAPE) {
  1316. evt.preventDefault();
  1317. this.hide();
  1318. }
  1319. }
  1320. }
  1321. };
  1322. /**
  1323. * Handles a click event on an item.
  1324. *
  1325. * @param {Event} evt The event that fired.
  1326. * @private
  1327. */
  1328. MaterialMenu.prototype.handleItemClick_ = function (evt) {
  1329. if (evt.target.hasAttribute('disabled')) {
  1330. evt.stopPropagation();
  1331. } else {
  1332. // Wait some time before closing menu, so the user can see the ripple.
  1333. this.closing_ = true;
  1334. window.setTimeout(function (evt) {
  1335. this.hide();
  1336. this.closing_ = false;
  1337. }.bind(this), this.Constant_.CLOSE_TIMEOUT);
  1338. }
  1339. };
  1340. /**
  1341. * Calculates the initial clip (for opening the menu) or final clip (for closing
  1342. * it), and applies it. This allows us to animate from or to the correct point,
  1343. * that is, the point it's aligned to in the "for" element.
  1344. *
  1345. * @param {number} height Height of the clip rectangle
  1346. * @param {number} width Width of the clip rectangle
  1347. * @private
  1348. */
  1349. MaterialMenu.prototype.applyClip_ = function (height, width) {
  1350. if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
  1351. // Do not clip.
  1352. this.element_.style.clip = '';
  1353. } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
  1354. // Clip to the top right corner of the menu.
  1355. this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
  1356. } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
  1357. // Clip to the bottom left corner of the menu.
  1358. this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)';
  1359. } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1360. // Clip to the bottom right corner of the menu.
  1361. this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)';
  1362. } else {
  1363. // Default: do not clip (same as clipping to the top left corner).
  1364. this.element_.style.clip = '';
  1365. }
  1366. };
  1367. /**
  1368. * Cleanup function to remove animation listeners.
  1369. *
  1370. * @param {Event} evt
  1371. * @private
  1372. */
  1373. MaterialMenu.prototype.removeAnimationEndListener_ = function (evt) {
  1374. evt.target.classList.remove(MaterialMenu.prototype.CssClasses_.IS_ANIMATING);
  1375. };
  1376. /**
  1377. * Adds an event listener to clean up after the animation ends.
  1378. *
  1379. * @private
  1380. */
  1381. MaterialMenu.prototype.addAnimationEndListener_ = function () {
  1382. this.element_.addEventListener('transitionend', this.removeAnimationEndListener_);
  1383. this.element_.addEventListener('webkitTransitionEnd', this.removeAnimationEndListener_);
  1384. };
  1385. /**
  1386. * Displays the menu.
  1387. *
  1388. * @public
  1389. */
  1390. MaterialMenu.prototype.show = function (evt) {
  1391. if (this.element_ && this.container_ && this.outline_) {
  1392. // Measure the inner element.
  1393. var height = this.element_.getBoundingClientRect().height;
  1394. var width = this.element_.getBoundingClientRect().width;
  1395. // Apply the inner element's size to the container and outline.
  1396. this.container_.style.width = width + 'px';
  1397. this.container_.style.height = height + 'px';
  1398. this.outline_.style.width = width + 'px';
  1399. this.outline_.style.height = height + 'px';
  1400. var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION;
  1401. // Calculate transition delays for individual menu items, so that they fade
  1402. // in one at a time.
  1403. var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
  1404. for (var i = 0; i < items.length; i++) {
  1405. var itemDelay = null;
  1406. if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1407. itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's';
  1408. } else {
  1409. itemDelay = items[i].offsetTop / height * transitionDuration + 's';
  1410. }
  1411. items[i].style.transitionDelay = itemDelay;
  1412. }
  1413. // Apply the initial clip to the text before we start animating.
  1414. this.applyClip_(height, width);
  1415. // Wait for the next frame, turn on animation, and apply the final clip.
  1416. // Also make it visible. This triggers the transitions.
  1417. window.requestAnimationFrame(function () {
  1418. this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
  1419. this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';
  1420. this.container_.classList.add(this.CssClasses_.IS_VISIBLE);
  1421. }.bind(this));
  1422. // Clean up after the animation is complete.
  1423. this.addAnimationEndListener_();
  1424. // Add a click listener to the document, to close the menu.
  1425. var callback = function (e) {
  1426. // Check to see if the document is processing the same event that
  1427. // displayed the menu in the first place. If so, do nothing.
  1428. // Also check to see if the menu is in the process of closing itself, and
  1429. // do nothing in that case.
  1430. // Also check if the clicked element is a menu item
  1431. // if so, do nothing.
  1432. if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) {
  1433. document.removeEventListener('click', callback);
  1434. this.hide();
  1435. }
  1436. }.bind(this);
  1437. document.addEventListener('click', callback);
  1438. }
  1439. };
  1440. MaterialMenu.prototype['show'] = MaterialMenu.prototype.show;
  1441. /**
  1442. * Hides the menu.
  1443. *
  1444. * @public
  1445. */
  1446. MaterialMenu.prototype.hide = function () {
  1447. if (this.element_ && this.container_ && this.outline_) {
  1448. var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
  1449. // Remove all transition delays; menu items fade out concurrently.
  1450. for (var i = 0; i < items.length; i++) {
  1451. items[i].style.removeProperty('transition-delay');
  1452. }
  1453. // Measure the inner element.
  1454. var rect = this.element_.getBoundingClientRect();
  1455. var height = rect.height;
  1456. var width = rect.width;
  1457. // Turn on animation, and apply the final clip. Also make invisible.
  1458. // This triggers the transitions.
  1459. this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
  1460. this.applyClip_(height, width);
  1461. this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);
  1462. // Clean up after the animation is complete.
  1463. this.addAnimationEndListener_();
  1464. }
  1465. };
  1466. MaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide;
  1467. /**
  1468. * Displays or hides the menu, depending on current state.
  1469. *
  1470. * @public
  1471. */
  1472. MaterialMenu.prototype.toggle = function (evt) {
  1473. if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
  1474. this.hide();
  1475. } else {
  1476. this.show(evt);
  1477. }
  1478. };
  1479. MaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;
  1480. // The component registers itself. It can assume componentHandler is available
  1481. // in the global scope.
  1482. componentHandler.register({
  1483. constructor: MaterialMenu,
  1484. classAsString: 'MaterialMenu',
  1485. cssClass: 'mdl-js-menu',
  1486. widget: true
  1487. });
  1488. /**
  1489. * @license
  1490. * Copyright 2015 Google Inc. All Rights Reserved.
  1491. *
  1492. * Licensed under the Apache License, Version 2.0 (the "License");
  1493. * you may not use this file except in compliance with the License.
  1494. * You may obtain a copy of the License at
  1495. *
  1496. * http://www.apache.org/licenses/LICENSE-2.0
  1497. *
  1498. * Unless required by applicable law or agreed to in writing, software
  1499. * distributed under the License is distributed on an "AS IS" BASIS,
  1500. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1501. * See the License for the specific language governing permissions and
  1502. * limitations under the License.
  1503. */
  1504. /**
  1505. * Class constructor for Progress MDL component.
  1506. * Implements MDL component design pattern defined at:
  1507. * https://github.com/jasonmayes/mdl-component-design-pattern
  1508. *
  1509. * @constructor
  1510. * @param {HTMLElement} element The element that will be upgraded.
  1511. */
  1512. var MaterialProgress = function MaterialProgress(element) {
  1513. this.element_ = element;
  1514. // Initialize instance.
  1515. this.init();
  1516. };
  1517. window['MaterialProgress'] = MaterialProgress;
  1518. /**
  1519. * Store constants in one place so they can be updated easily.
  1520. *
  1521. * @enum {string | number}
  1522. * @private
  1523. */
  1524. MaterialProgress.prototype.Constant_ = {};
  1525. /**
  1526. * Store strings for class names defined by this component that are used in
  1527. * JavaScript. This allows us to simply change it in one place should we
  1528. * decide to modify at a later date.
  1529. *
  1530. * @enum {string}
  1531. * @private
  1532. */
  1533. MaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' };
  1534. /**
  1535. * Set the current progress of the progressbar.
  1536. *
  1537. * @param {number} p Percentage of the progress (0-100)
  1538. * @public
  1539. */
  1540. MaterialProgress.prototype.setProgress = function (p) {
  1541. if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) {
  1542. return;
  1543. }
  1544. this.progressbar_.style.width = p + '%';
  1545. };
  1546. MaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress;
  1547. /**
  1548. * Set the current progress of the buffer.
  1549. *
  1550. * @param {number} p Percentage of the buffer (0-100)
  1551. * @public
  1552. */
  1553. MaterialProgress.prototype.setBuffer = function (p) {
  1554. this.bufferbar_.style.width = p + '%';
  1555. this.auxbar_.style.width = 100 - p + '%';
  1556. };
  1557. MaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer;
  1558. /**
  1559. * Initialize element.
  1560. */
  1561. MaterialProgress.prototype.init = function () {
  1562. if (this.element_) {
  1563. var el = document.createElement('div');
  1564. el.className = 'progressbar bar bar1';
  1565. this.element_.appendChild(el);
  1566. this.progressbar_ = el;
  1567. el = document.createElement('div');
  1568. el.className = 'bufferbar bar bar2';
  1569. this.element_.appendChild(el);
  1570. this.bufferbar_ = el;
  1571. el = document.createElement('div');
  1572. el.className = 'auxbar bar bar3';
  1573. this.element_.appendChild(el);
  1574. this.auxbar_ = el;
  1575. this.progressbar_.style.width = '0%';
  1576. this.bufferbar_.style.width = '100%';
  1577. this.auxbar_.style.width = '0%';
  1578. this.element_.classList.add('is-upgraded');
  1579. }
  1580. };
  1581. // The component registers itself. It can assume componentHandler is available
  1582. // in the global scope.
  1583. componentHandler.register({
  1584. constructor: MaterialProgress,
  1585. classAsString: 'MaterialProgress',
  1586. cssClass: 'mdl-js-progress',
  1587. widget: true
  1588. });
  1589. /**
  1590. * @license
  1591. * Copyright 2015 Google Inc. All Rights Reserved.
  1592. *
  1593. * Licensed under the Apache License, Version 2.0 (the "License");
  1594. * you may not use this file except in compliance with the License.
  1595. * You may obtain a copy of the License at
  1596. *
  1597. * http://www.apache.org/licenses/LICENSE-2.0
  1598. *
  1599. * Unless required by applicable law or agreed to in writing, software
  1600. * distributed under the License is distributed on an "AS IS" BASIS,
  1601. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1602. * See the License for the specific language governing permissions and
  1603. * limitations under the License.
  1604. */
  1605. /**
  1606. * Class constructor for Radio MDL component.
  1607. * Implements MDL component design pattern defined at:
  1608. * https://github.com/jasonmayes/mdl-component-design-pattern
  1609. *
  1610. * @constructor
  1611. * @param {HTMLElement} element The element that will be upgraded.
  1612. */
  1613. var MaterialRadio = function MaterialRadio(element) {
  1614. this.element_ = element;
  1615. // Initialize instance.
  1616. this.init();
  1617. };
  1618. window['MaterialRadio'] = MaterialRadio;
  1619. /**
  1620. * Store constants in one place so they can be updated easily.
  1621. *
  1622. * @enum {string | number}
  1623. * @private
  1624. */
  1625. MaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
  1626. /**
  1627. * Store strings for class names defined by this component that are used in
  1628. * JavaScript. This allows us to simply change it in one place should we
  1629. * decide to modify at a later date.
  1630. *
  1631. * @enum {string}
  1632. * @private
  1633. */
  1634. MaterialRadio.prototype.CssClasses_ = {
  1635. IS_FOCUSED: 'is-focused',
  1636. IS_DISABLED: 'is-disabled',
  1637. IS_CHECKED: 'is-checked',
  1638. IS_UPGRADED: 'is-upgraded',
  1639. JS_RADIO: 'mdl-js-radio',
  1640. RADIO_BTN: 'mdl-radio__button',
  1641. RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle',
  1642. RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle',
  1643. RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  1644. RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  1645. RIPPLE_CONTAINER: 'mdl-radio__ripple-container',
  1646. RIPPLE_CENTER: 'mdl-ripple--center',
  1647. RIPPLE: 'mdl-ripple'
  1648. };
  1649. /**
  1650. * Handle change of state.
  1651. *
  1652. * @param {Event} event The event that fired.
  1653. * @private
  1654. */
  1655. MaterialRadio.prototype.onChange_ = function (event) {
  1656. // Since other radio buttons don't get change events, we need to look for
  1657. // them to update their classes.
  1658. var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO);
  1659. for (var i = 0; i < radios.length; i++) {
  1660. var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN);
  1661. // Different name == different group, so no point updating those.
  1662. if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) {
  1663. if (typeof radios[i]['MaterialRadio'] !== 'undefined') {
  1664. radios[i]['MaterialRadio'].updateClasses_();
  1665. }
  1666. }
  1667. }
  1668. };
  1669. /**
  1670. * Handle focus.
  1671. *
  1672. * @param {Event} event The event that fired.
  1673. * @private
  1674. */
  1675. MaterialRadio.prototype.onFocus_ = function (event) {
  1676. this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  1677. };
  1678. /**
  1679. * Handle lost focus.
  1680. *
  1681. * @param {Event} event The event that fired.
  1682. * @private
  1683. */
  1684. MaterialRadio.prototype.onBlur_ = function (event) {
  1685. this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  1686. };
  1687. /**
  1688. * Handle mouseup.
  1689. *
  1690. * @param {Event} event The event that fired.
  1691. * @private
  1692. */
  1693. MaterialRadio.prototype.onMouseup_ = function (event) {
  1694. this.blur_();
  1695. };
  1696. /**
  1697. * Update classes.
  1698. *
  1699. * @private
  1700. */
  1701. MaterialRadio.prototype.updateClasses_ = function () {
  1702. this.checkDisabled();
  1703. this.checkToggleState();
  1704. };
  1705. /**
  1706. * Add blur.
  1707. *
  1708. * @private
  1709. */
  1710. MaterialRadio.prototype.blur_ = function () {
  1711. // TODO: figure out why there's a focus event being fired after our blur,
  1712. // so that we can avoid this hack.
  1713. window.setTimeout(function () {
  1714. this.btnElement_.blur();
  1715. }.bind(this), this.Constant_.TINY_TIMEOUT);
  1716. };
  1717. // Public methods.
  1718. /**
  1719. * Check the components disabled state.
  1720. *
  1721. * @public
  1722. */
  1723. MaterialRadio.prototype.checkDisabled = function () {
  1724. if (this.btnElement_.disabled) {
  1725. this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  1726. } else {
  1727. this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  1728. }
  1729. };
  1730. MaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled;
  1731. /**
  1732. * Check the components toggled state.
  1733. *
  1734. * @public
  1735. */
  1736. MaterialRadio.prototype.checkToggleState = function () {
  1737. if (this.btnElement_.checked) {
  1738. this.element_.classList.add(this.CssClasses_.IS_CHECKED);
  1739. } else {
  1740. this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
  1741. }
  1742. };
  1743. MaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState;
  1744. /**
  1745. * Disable radio.
  1746. *
  1747. * @public
  1748. */
  1749. MaterialRadio.prototype.disable = function () {
  1750. this.btnElement_.disabled = true;
  1751. this.updateClasses_();
  1752. };
  1753. MaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable;
  1754. /**
  1755. * Enable radio.
  1756. *
  1757. * @public
  1758. */
  1759. MaterialRadio.prototype.enable = function () {
  1760. this.btnElement_.disabled = false;
  1761. this.updateClasses_();
  1762. };
  1763. MaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable;
  1764. /**
  1765. * Check radio.
  1766. *
  1767. * @public
  1768. */
  1769. MaterialRadio.prototype.check = function () {
  1770. this.btnElement_.checked = true;
  1771. this.onChange_(null);
  1772. };
  1773. MaterialRadio.prototype['check'] = MaterialRadio.prototype.check;
  1774. /**
  1775. * Uncheck radio.
  1776. *
  1777. * @public
  1778. */
  1779. MaterialRadio.prototype.uncheck = function () {
  1780. this.btnElement_.checked = false;
  1781. this.onChange_(null);
  1782. };
  1783. MaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck;
  1784. /**
  1785. * Initialize element.
  1786. */
  1787. MaterialRadio.prototype.init = function () {
  1788. if (this.element_) {
  1789. this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN);
  1790. this.boundChangeHandler_ = this.onChange_.bind(this);
  1791. this.boundFocusHandler_ = this.onChange_.bind(this);
  1792. this.boundBlurHandler_ = this.onBlur_.bind(this);
  1793. this.boundMouseUpHandler_ = this.onMouseup_.bind(this);
  1794. var outerCircle = document.createElement('span');
  1795. outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE);
  1796. var innerCircle = document.createElement('span');
  1797. innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE);
  1798. this.element_.appendChild(outerCircle);
  1799. this.element_.appendChild(innerCircle);
  1800. var rippleContainer;
  1801. if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  1802. this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  1803. rippleContainer = document.createElement('span');
  1804. rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  1805. rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT);
  1806. rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER);
  1807. rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_);
  1808. var ripple = document.createElement('span');
  1809. ripple.classList.add(this.CssClasses_.RIPPLE);
  1810. rippleContainer.appendChild(ripple);
  1811. this.element_.appendChild(rippleContainer);
  1812. }
  1813. this.btnElement_.addEventListener('change', this.boundChangeHandler_);
  1814. this.btnElement_.addEventListener('focus', this.boundFocusHandler_);
  1815. this.btnElement_.addEventListener('blur', this.boundBlurHandler_);
  1816. this.element_.addEventListener('mouseup', this.boundMouseUpHandler_);
  1817. this.updateClasses_();
  1818. this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  1819. }
  1820. };
  1821. // The component registers itself. It can assume componentHandler is available
  1822. // in the global scope.
  1823. componentHandler.register({
  1824. constructor: MaterialRadio,
  1825. classAsString: 'MaterialRadio',
  1826. cssClass: 'mdl-js-radio',
  1827. widget: true
  1828. });
  1829. /**
  1830. * @license
  1831. * Copyright 2015 Google Inc. All Rights Reserved.
  1832. *
  1833. * Licensed under the Apache License, Version 2.0 (the "License");
  1834. * you may not use this file except in compliance with the License.
  1835. * You may obtain a copy of the License at
  1836. *
  1837. * http://www.apache.org/licenses/LICENSE-2.0
  1838. *
  1839. * Unless required by applicable law or agreed to in writing, software
  1840. * distributed under the License is distributed on an "AS IS" BASIS,
  1841. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1842. * See the License for the specific language governing permissions and
  1843. * limitations under the License.
  1844. */
  1845. /**
  1846. * Class constructor for Slider MDL component.
  1847. * Implements MDL component design pattern defined at:
  1848. * https://github.com/jasonmayes/mdl-component-design-pattern
  1849. *
  1850. * @constructor
  1851. * @param {HTMLElement} element The element that will be upgraded.
  1852. */
  1853. var MaterialSlider = function MaterialSlider(element) {
  1854. this.element_ = element;
  1855. // Browser feature detection.
  1856. this.isIE_ = window.navigator.msPointerEnabled;
  1857. // Initialize instance.
  1858. this.init();
  1859. };
  1860. window['MaterialSlider'] = MaterialSlider;
  1861. /**
  1862. * Store constants in one place so they can be updated easily.
  1863. *
  1864. * @enum {string | number}
  1865. * @private
  1866. */
  1867. MaterialSlider.prototype.Constant_ = {};
  1868. /**
  1869. * Store strings for class names defined by this component that are used in
  1870. * JavaScript. This allows us to simply change it in one place should we
  1871. * decide to modify at a later date.
  1872. *
  1873. * @enum {string}
  1874. * @private
  1875. */
  1876. MaterialSlider.prototype.CssClasses_ = {
  1877. IE_CONTAINER: 'mdl-slider__ie-container',
  1878. SLIDER_CONTAINER: 'mdl-slider__container',
  1879. BACKGROUND_FLEX: 'mdl-slider__background-flex',
  1880. BACKGROUND_LOWER: 'mdl-slider__background-lower',
  1881. BACKGROUND_UPPER: 'mdl-slider__background-upper',
  1882. IS_LOWEST_VALUE: 'is-lowest-value',
  1883. IS_UPGRADED: 'is-upgraded'
  1884. };
  1885. /**
  1886. * Handle input on element.
  1887. *
  1888. * @param {Event} event The event that fired.
  1889. * @private
  1890. */
  1891. MaterialSlider.prototype.onInput_ = function (event) {
  1892. this.updateValueStyles_();
  1893. };
  1894. /**
  1895. * Handle change on element.
  1896. *
  1897. * @param {Event} event The event that fired.
  1898. * @private
  1899. */
  1900. MaterialSlider.prototype.onChange_ = function (event) {
  1901. this.updateValueStyles_();
  1902. };
  1903. /**
  1904. * Handle mouseup on element.
  1905. *
  1906. * @param {Event} event The event that fired.
  1907. * @private
  1908. */
  1909. MaterialSlider.prototype.onMouseUp_ = function (event) {
  1910. event.target.blur();
  1911. };
  1912. /**
  1913. * Handle mousedown on container element.
  1914. * This handler is purpose is to not require the use to click
  1915. * exactly on the 2px slider element, as FireFox seems to be very
  1916. * strict about this.
  1917. *
  1918. * @param {Event} event The event that fired.
  1919. * @private
  1920. * @suppress {missingProperties}
  1921. */
  1922. MaterialSlider.prototype.onContainerMouseDown_ = function (event) {
  1923. // If this click is not on the parent element (but rather some child)
  1924. // ignore. It may still bubble up.
  1925. if (event.target !== this.element_.parentElement) {
  1926. return;
  1927. }
  1928. // Discard the original event and create a new event that
  1929. // is on the slider element.
  1930. event.preventDefault();
  1931. var newEvent = new MouseEvent('mousedown', {
  1932. target: event.target,
  1933. buttons: event.buttons,
  1934. clientX: event.clientX,
  1935. clientY: this.element_.getBoundingClientRect().y
  1936. });
  1937. this.element_.dispatchEvent(newEvent);
  1938. };
  1939. /**
  1940. * Handle updating of values.
  1941. *
  1942. * @private
  1943. */
  1944. MaterialSlider.prototype.updateValueStyles_ = function () {
  1945. // Calculate and apply percentages to div structure behind slider.
  1946. var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min);
  1947. if (fraction === 0) {
  1948. this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE);
  1949. } else {
  1950. this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE);
  1951. }
  1952. if (!this.isIE_) {
  1953. this.backgroundLower_.style.flex = fraction;
  1954. this.backgroundLower_.style.webkitFlex = fraction;
  1955. this.backgroundUpper_.style.flex = 1 - fraction;
  1956. this.backgroundUpper_.style.webkitFlex = 1 - fraction;
  1957. }
  1958. };
  1959. // Public methods.
  1960. /**
  1961. * Disable slider.
  1962. *
  1963. * @public
  1964. */
  1965. MaterialSlider.prototype.disable = function () {
  1966. this.element_.disabled = true;
  1967. };
  1968. MaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable;
  1969. /**
  1970. * Enable slider.
  1971. *
  1972. * @public
  1973. */
  1974. MaterialSlider.prototype.enable = function () {
  1975. this.element_.disabled = false;
  1976. };
  1977. MaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable;
  1978. /**
  1979. * Update slider value.
  1980. *
  1981. * @param {number} value The value to which to set the control (optional).
  1982. * @public
  1983. */
  1984. MaterialSlider.prototype.change = function (value) {
  1985. if (typeof value !== 'undefined') {
  1986. this.element_.value = value;
  1987. }
  1988. this.updateValueStyles_();
  1989. };
  1990. MaterialSlider.prototype['change'] = MaterialSlider.prototype.change;
  1991. /**
  1992. * Initialize element.
  1993. */
  1994. MaterialSlider.prototype.init = function () {
  1995. if (this.element_) {
  1996. if (this.isIE_) {
  1997. // Since we need to specify a very large height in IE due to
  1998. // implementation limitations, we add a parent here that trims it down to
  1999. // a reasonable size.
  2000. var containerIE = document.createElement('div');
  2001. containerIE.classList.add(this.CssClasses_.IE_CONTAINER);
  2002. this.element_.parentElement.insertBefore(containerIE, this.element_);
  2003. this.element_.parentElement.removeChild(this.element_);
  2004. containerIE.appendChild(this.element_);
  2005. } else {
  2006. // For non-IE browsers, we need a div structure that sits behind the
  2007. // slider and allows us to style the left and right sides of it with
  2008. // different colors.
  2009. var container = document.createElement('div');
  2010. container.classList.add(this.CssClasses_.SLIDER_CONTAINER);
  2011. this.element_.parentElement.insertBefore(container, this.element_);
  2012. this.element_.parentElement.removeChild(this.element_);
  2013. container.appendChild(this.element_);
  2014. var backgroundFlex = document.createElement('div');
  2015. backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX);
  2016. container.appendChild(backgroundFlex);
  2017. this.backgroundLower_ = document.createElement('div');
  2018. this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER);
  2019. backgroundFlex.appendChild(this.backgroundLower_);
  2020. this.backgroundUpper_ = document.createElement('div');
  2021. this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER);
  2022. backgroundFlex.appendChild(this.backgroundUpper_);
  2023. }
  2024. this.boundInputHandler = this.onInput_.bind(this);
  2025. this.boundChangeHandler = this.onChange_.bind(this);
  2026. this.boundMouseUpHandler = this.onMouseUp_.bind(this);
  2027. this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this);
  2028. this.element_.addEventListener('input', this.boundInputHandler);
  2029. this.element_.addEventListener('change', this.boundChangeHandler);
  2030. this.element_.addEventListener('mouseup', this.boundMouseUpHandler);
  2031. this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler);
  2032. this.updateValueStyles_();
  2033. this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  2034. }
  2035. };
  2036. // The component registers itself. It can assume componentHandler is available
  2037. // in the global scope.
  2038. componentHandler.register({
  2039. constructor: MaterialSlider,
  2040. classAsString: 'MaterialSlider',
  2041. cssClass: 'mdl-js-slider',
  2042. widget: true
  2043. });
  2044. /**
  2045. * Copyright 2015 Google Inc. All Rights Reserved.
  2046. *
  2047. * Licensed under the Apache License, Version 2.0 (the "License");
  2048. * you may not use this file except in compliance with the License.
  2049. * You may obtain a copy of the License at
  2050. *
  2051. * http://www.apache.org/licenses/LICENSE-2.0
  2052. *
  2053. * Unless required by applicable law or agreed to in writing, software
  2054. * distributed under the License is distributed on an "AS IS" BASIS,
  2055. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2056. * See the License for the specific language governing permissions and
  2057. * limitations under the License.
  2058. */
  2059. /**
  2060. * Class constructor for Snackbar MDL component.
  2061. * Implements MDL component design pattern defined at:
  2062. * https://github.com/jasonmayes/mdl-component-design-pattern
  2063. *
  2064. * @constructor
  2065. * @param {HTMLElement} element The element that will be upgraded.
  2066. */
  2067. var MaterialSnackbar = function MaterialSnackbar(element) {
  2068. this.element_ = element;
  2069. this.textElement_ = this.element_.querySelector('.' + this.cssClasses_.MESSAGE);
  2070. this.actionElement_ = this.element_.querySelector('.' + this.cssClasses_.ACTION);
  2071. if (!this.textElement_) {
  2072. throw new Error('There must be a message element for a snackbar.');
  2073. }
  2074. if (!this.actionElement_) {
  2075. throw new Error('There must be an action element for a snackbar.');
  2076. }
  2077. this.active = false;
  2078. this.actionHandler_ = undefined;
  2079. this.message_ = undefined;
  2080. this.actionText_ = undefined;
  2081. this.queuedNotifications_ = [];
  2082. this.setActionHidden_(true);
  2083. };
  2084. window['MaterialSnackbar'] = MaterialSnackbar;
  2085. /**
  2086. * Store constants in one place so they can be updated easily.
  2087. *
  2088. * @enum {string | number}
  2089. * @private
  2090. */
  2091. MaterialSnackbar.prototype.Constant_ = {
  2092. // The duration of the snackbar show/hide animation, in ms.
  2093. ANIMATION_LENGTH: 250
  2094. };
  2095. /**
  2096. * Store strings for class names defined by this component that are used in
  2097. * JavaScript. This allows us to simply change it in one place should we
  2098. * decide to modify at a later date.
  2099. *
  2100. * @enum {string}
  2101. * @private
  2102. */
  2103. MaterialSnackbar.prototype.cssClasses_ = {
  2104. SNACKBAR: 'mdl-snackbar',
  2105. MESSAGE: 'mdl-snackbar__text',
  2106. ACTION: 'mdl-snackbar__action',
  2107. ACTIVE: 'mdl-snackbar--active'
  2108. };
  2109. /**
  2110. * Display the snackbar.
  2111. *
  2112. * @private
  2113. */
  2114. MaterialSnackbar.prototype.displaySnackbar_ = function () {
  2115. this.element_.setAttribute('aria-hidden', 'true');
  2116. if (this.actionHandler_) {
  2117. this.actionElement_.textContent = this.actionText_;
  2118. this.actionElement_.addEventListener('click', this.actionHandler_);
  2119. this.setActionHidden_(false);
  2120. }
  2121. this.textElement_.textContent = this.message_;
  2122. this.element_.classList.add(this.cssClasses_.ACTIVE);
  2123. this.element_.setAttribute('aria-hidden', 'false');
  2124. setTimeout(this.cleanup_.bind(this), this.timeout_);
  2125. };
  2126. /**
  2127. * Show the snackbar.
  2128. *
  2129. * @param {Object} data The data for the notification.
  2130. * @public
  2131. */
  2132. MaterialSnackbar.prototype.showSnackbar = function (data) {
  2133. if (data === undefined) {
  2134. throw new Error('Please provide a data object with at least a message to display.');
  2135. }
  2136. if (data['message'] === undefined) {
  2137. throw new Error('Please provide a message to be displayed.');
  2138. }
  2139. if (data['actionHandler'] && !data['actionText']) {
  2140. throw new Error('Please provide action text with the handler.');
  2141. }
  2142. if (this.active) {
  2143. this.queuedNotifications_.push(data);
  2144. } else {
  2145. this.active = true;
  2146. this.message_ = data['message'];
  2147. if (data['timeout']) {
  2148. this.timeout_ = data['timeout'];
  2149. } else {
  2150. this.timeout_ = 2750;
  2151. }
  2152. if (data['actionHandler']) {
  2153. this.actionHandler_ = data['actionHandler'];
  2154. }
  2155. if (data['actionText']) {
  2156. this.actionText_ = data['actionText'];
  2157. }
  2158. this.displaySnackbar_();
  2159. }
  2160. };
  2161. MaterialSnackbar.prototype['showSnackbar'] = MaterialSnackbar.prototype.showSnackbar;
  2162. /**
  2163. * Check if the queue has items within it.
  2164. * If it does, display the next entry.
  2165. *
  2166. * @private
  2167. */
  2168. MaterialSnackbar.prototype.checkQueue_ = function () {
  2169. if (this.queuedNotifications_.length > 0) {
  2170. this.showSnackbar(this.queuedNotifications_.shift());
  2171. }
  2172. };
  2173. /**
  2174. * Cleanup the snackbar event listeners and accessiblity attributes.
  2175. *
  2176. * @private
  2177. */
  2178. MaterialSnackbar.prototype.cleanup_ = function () {
  2179. this.element_.classList.remove(this.cssClasses_.ACTIVE);
  2180. setTimeout(function () {
  2181. this.element_.setAttribute('aria-hidden', 'true');
  2182. this.textElement_.textContent = '';
  2183. if (!Boolean(this.actionElement_.getAttribute('aria-hidden'))) {
  2184. this.setActionHidden_(true);
  2185. this.actionElement_.textContent = '';
  2186. this.actionElement_.removeEventListener('click', this.actionHandler_);
  2187. }
  2188. this.actionHandler_ = undefined;
  2189. this.message_ = undefined;
  2190. this.actionText_ = undefined;
  2191. this.active = false;
  2192. this.checkQueue_();
  2193. }.bind(this), this.Constant_.ANIMATION_LENGTH);
  2194. };
  2195. /**
  2196. * Set the action handler hidden state.
  2197. *
  2198. * @param {boolean} value
  2199. * @private
  2200. */
  2201. MaterialSnackbar.prototype.setActionHidden_ = function (value) {
  2202. if (value) {
  2203. this.actionElement_.setAttribute('aria-hidden', 'true');
  2204. } else {
  2205. this.actionElement_.removeAttribute('aria-hidden');
  2206. }
  2207. };
  2208. // The component registers itself. It can assume componentHandler is available
  2209. // in the global scope.
  2210. componentHandler.register({
  2211. constructor: MaterialSnackbar,
  2212. classAsString: 'MaterialSnackbar',
  2213. cssClass: 'mdl-js-snackbar',
  2214. widget: true
  2215. });
  2216. /**
  2217. * @license
  2218. * Copyright 2015 Google Inc. All Rights Reserved.
  2219. *
  2220. * Licensed under the Apache License, Version 2.0 (the "License");
  2221. * you may not use this file except in compliance with the License.
  2222. * You may obtain a copy of the License at
  2223. *
  2224. * http://www.apache.org/licenses/LICENSE-2.0
  2225. *
  2226. * Unless required by applicable law or agreed to in writing, software
  2227. * distributed under the License is distributed on an "AS IS" BASIS,
  2228. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2229. * See the License for the specific language governing permissions and
  2230. * limitations under the License.
  2231. */
  2232. /**
  2233. * Class constructor for Spinner MDL component.
  2234. * Implements MDL component design pattern defined at:
  2235. * https://github.com/jasonmayes/mdl-component-design-pattern
  2236. *
  2237. * @param {HTMLElement} element The element that will be upgraded.
  2238. * @constructor
  2239. */
  2240. var MaterialSpinner = function MaterialSpinner(element) {
  2241. this.element_ = element;
  2242. // Initialize instance.
  2243. this.init();
  2244. };
  2245. window['MaterialSpinner'] = MaterialSpinner;
  2246. /**
  2247. * Store constants in one place so they can be updated easily.
  2248. *
  2249. * @enum {string | number}
  2250. * @private
  2251. */
  2252. MaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 };
  2253. /**
  2254. * Store strings for class names defined by this component that are used in
  2255. * JavaScript. This allows us to simply change it in one place should we
  2256. * decide to modify at a later date.
  2257. *
  2258. * @enum {string}
  2259. * @private
  2260. */
  2261. MaterialSpinner.prototype.CssClasses_ = {
  2262. MDL_SPINNER_LAYER: 'mdl-spinner__layer',
  2263. MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper',
  2264. MDL_SPINNER_CIRCLE: 'mdl-spinner__circle',
  2265. MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch',
  2266. MDL_SPINNER_LEFT: 'mdl-spinner__left',
  2267. MDL_SPINNER_RIGHT: 'mdl-spinner__right'
  2268. };
  2269. /**
  2270. * Auxiliary method to create a spinner layer.
  2271. *
  2272. * @param {number} index Index of the layer to be created.
  2273. * @public
  2274. */
  2275. MaterialSpinner.prototype.createLayer = function (index) {
  2276. var layer = document.createElement('div');
  2277. layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER);
  2278. layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index);
  2279. var leftClipper = document.createElement('div');
  2280. leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);
  2281. leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT);
  2282. var gapPatch = document.createElement('div');
  2283. gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH);
  2284. var rightClipper = document.createElement('div');
  2285. rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);
  2286. rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT);
  2287. var circleOwners = [
  2288. leftClipper,
  2289. gapPatch,
  2290. rightClipper
  2291. ];
  2292. for (var i = 0; i < circleOwners.length; i++) {
  2293. var circle = document.createElement('div');
  2294. circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE);
  2295. circleOwners[i].appendChild(circle);
  2296. }
  2297. layer.appendChild(leftClipper);
  2298. layer.appendChild(gapPatch);
  2299. layer.appendChild(rightClipper);
  2300. this.element_.appendChild(layer);
  2301. };
  2302. MaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer;
  2303. /**
  2304. * Stops the spinner animation.
  2305. * Public method for users who need to stop the spinner for any reason.
  2306. *
  2307. * @public
  2308. */
  2309. MaterialSpinner.prototype.stop = function () {
  2310. this.element_.classList.remove('is-active');
  2311. };
  2312. MaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop;
  2313. /**
  2314. * Starts the spinner animation.
  2315. * Public method for users who need to manually start the spinner for any reason
  2316. * (instead of just adding the 'is-active' class to their markup).
  2317. *
  2318. * @public
  2319. */
  2320. MaterialSpinner.prototype.start = function () {
  2321. this.element_.classList.add('is-active');
  2322. };
  2323. MaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start;
  2324. /**
  2325. * Initialize element.
  2326. */
  2327. MaterialSpinner.prototype.init = function () {
  2328. if (this.element_) {
  2329. for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) {
  2330. this.createLayer(i);
  2331. }
  2332. this.element_.classList.add('is-upgraded');
  2333. }
  2334. };
  2335. // The component registers itself. It can assume componentHandler is available
  2336. // in the global scope.
  2337. componentHandler.register({
  2338. constructor: MaterialSpinner,
  2339. classAsString: 'MaterialSpinner',
  2340. cssClass: 'mdl-js-spinner',
  2341. widget: true
  2342. });
  2343. /**
  2344. * @license
  2345. * Copyright 2015 Google Inc. All Rights Reserved.
  2346. *
  2347. * Licensed under the Apache License, Version 2.0 (the "License");
  2348. * you may not use this file except in compliance with the License.
  2349. * You may obtain a copy of the License at
  2350. *
  2351. * http://www.apache.org/licenses/LICENSE-2.0
  2352. *
  2353. * Unless required by applicable law or agreed to in writing, software
  2354. * distributed under the License is distributed on an "AS IS" BASIS,
  2355. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2356. * See the License for the specific language governing permissions and
  2357. * limitations under the License.
  2358. */
  2359. /**
  2360. * Class constructor for Checkbox MDL component.
  2361. * Implements MDL component design pattern defined at:
  2362. * https://github.com/jasonmayes/mdl-component-design-pattern
  2363. *
  2364. * @constructor
  2365. * @param {HTMLElement} element The element that will be upgraded.
  2366. */
  2367. var MaterialSwitch = function MaterialSwitch(element) {
  2368. this.element_ = element;
  2369. // Initialize instance.
  2370. this.init();
  2371. };
  2372. window['MaterialSwitch'] = MaterialSwitch;
  2373. /**
  2374. * Store constants in one place so they can be updated easily.
  2375. *
  2376. * @enum {string | number}
  2377. * @private
  2378. */
  2379. MaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
  2380. /**
  2381. * Store strings for class names defined by this component that are used in
  2382. * JavaScript. This allows us to simply change it in one place should we
  2383. * decide to modify at a later date.
  2384. *
  2385. * @enum {string}
  2386. * @private
  2387. */
  2388. MaterialSwitch.prototype.CssClasses_ = {
  2389. INPUT: 'mdl-switch__input',
  2390. TRACK: 'mdl-switch__track',
  2391. THUMB: 'mdl-switch__thumb',
  2392. FOCUS_HELPER: 'mdl-switch__focus-helper',
  2393. RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  2394. RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  2395. RIPPLE_CONTAINER: 'mdl-switch__ripple-container',
  2396. RIPPLE_CENTER: 'mdl-ripple--center',
  2397. RIPPLE: 'mdl-ripple',
  2398. IS_FOCUSED: 'is-focused',
  2399. IS_DISABLED: 'is-disabled',
  2400. IS_CHECKED: 'is-checked'
  2401. };
  2402. /**
  2403. * Handle change of state.
  2404. *
  2405. * @param {Event} event The event that fired.
  2406. * @private
  2407. */
  2408. MaterialSwitch.prototype.onChange_ = function (event) {
  2409. this.updateClasses_();
  2410. };
  2411. /**
  2412. * Handle focus of element.
  2413. *
  2414. * @param {Event} event The event that fired.
  2415. * @private
  2416. */
  2417. MaterialSwitch.prototype.onFocus_ = function (event) {
  2418. this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  2419. };
  2420. /**
  2421. * Handle lost focus of element.
  2422. *
  2423. * @param {Event} event The event that fired.
  2424. * @private
  2425. */
  2426. MaterialSwitch.prototype.onBlur_ = function (event) {
  2427. this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  2428. };
  2429. /**
  2430. * Handle mouseup.
  2431. *
  2432. * @param {Event} event The event that fired.
  2433. * @private
  2434. */
  2435. MaterialSwitch.prototype.onMouseUp_ = function (event) {
  2436. this.blur_();
  2437. };
  2438. /**
  2439. * Handle class updates.
  2440. *
  2441. * @private
  2442. */
  2443. MaterialSwitch.prototype.updateClasses_ = function () {
  2444. this.checkDisabled();
  2445. this.checkToggleState();
  2446. };
  2447. /**
  2448. * Add blur.
  2449. *
  2450. * @private
  2451. */
  2452. MaterialSwitch.prototype.blur_ = function () {
  2453. // TODO: figure out why there's a focus event being fired after our blur,
  2454. // so that we can avoid this hack.
  2455. window.setTimeout(function () {
  2456. this.inputElement_.blur();
  2457. }.bind(this), this.Constant_.TINY_TIMEOUT);
  2458. };
  2459. // Public methods.
  2460. /**
  2461. * Check the components disabled state.
  2462. *
  2463. * @public
  2464. */
  2465. MaterialSwitch.prototype.checkDisabled = function () {
  2466. if (this.inputElement_.disabled) {
  2467. this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  2468. } else {
  2469. this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  2470. }
  2471. };
  2472. MaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled;
  2473. /**
  2474. * Check the components toggled state.
  2475. *
  2476. * @public
  2477. */
  2478. MaterialSwitch.prototype.checkToggleState = function () {
  2479. if (this.inputElement_.checked) {
  2480. this.element_.classList.add(this.CssClasses_.IS_CHECKED);
  2481. } else {
  2482. this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
  2483. }
  2484. };
  2485. MaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState;
  2486. /**
  2487. * Disable switch.
  2488. *
  2489. * @public
  2490. */
  2491. MaterialSwitch.prototype.disable = function () {
  2492. this.inputElement_.disabled = true;
  2493. this.updateClasses_();
  2494. };
  2495. MaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable;
  2496. /**
  2497. * Enable switch.
  2498. *
  2499. * @public
  2500. */
  2501. MaterialSwitch.prototype.enable = function () {
  2502. this.inputElement_.disabled = false;
  2503. this.updateClasses_();
  2504. };
  2505. MaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable;
  2506. /**
  2507. * Activate switch.
  2508. *
  2509. * @public
  2510. */
  2511. MaterialSwitch.prototype.on = function () {
  2512. this.inputElement_.checked = true;
  2513. this.updateClasses_();
  2514. };
  2515. MaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on;
  2516. /**
  2517. * Deactivate switch.
  2518. *
  2519. * @public
  2520. */
  2521. MaterialSwitch.prototype.off = function () {
  2522. this.inputElement_.checked = false;
  2523. this.updateClasses_();
  2524. };
  2525. MaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off;
  2526. /**
  2527. * Initialize element.
  2528. */
  2529. MaterialSwitch.prototype.init = function () {
  2530. if (this.element_) {
  2531. this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
  2532. var track = document.createElement('div');
  2533. track.classList.add(this.CssClasses_.TRACK);
  2534. var thumb = document.createElement('div');
  2535. thumb.classList.add(this.CssClasses_.THUMB);
  2536. var focusHelper = document.createElement('span');
  2537. focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER);
  2538. thumb.appendChild(focusHelper);
  2539. this.element_.appendChild(track);
  2540. this.element_.appendChild(thumb);
  2541. this.boundMouseUpHandler = this.onMouseUp_.bind(this);
  2542. if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  2543. this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  2544. this.rippleContainerElement_ = document.createElement('span');
  2545. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  2546. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);
  2547. this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);
  2548. this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler);
  2549. var ripple = document.createElement('span');
  2550. ripple.classList.add(this.CssClasses_.RIPPLE);
  2551. this.rippleContainerElement_.appendChild(ripple);
  2552. this.element_.appendChild(this.rippleContainerElement_);
  2553. }
  2554. this.boundChangeHandler = this.onChange_.bind(this);
  2555. this.boundFocusHandler = this.onFocus_.bind(this);
  2556. this.boundBlurHandler = this.onBlur_.bind(this);
  2557. this.inputElement_.addEventListener('change', this.boundChangeHandler);
  2558. this.inputElement_.addEventListener('focus', this.boundFocusHandler);
  2559. this.inputElement_.addEventListener('blur', this.boundBlurHandler);
  2560. this.element_.addEventListener('mouseup', this.boundMouseUpHandler);
  2561. this.updateClasses_();
  2562. this.element_.classList.add('is-upgraded');
  2563. }
  2564. };
  2565. // The component registers itself. It can assume componentHandler is available
  2566. // in the global scope.
  2567. componentHandler.register({
  2568. constructor: MaterialSwitch,
  2569. classAsString: 'MaterialSwitch',
  2570. cssClass: 'mdl-js-switch',
  2571. widget: true
  2572. });
  2573. /**
  2574. * @license
  2575. * Copyright 2015 Google Inc. All Rights Reserved.
  2576. *
  2577. * Licensed under the Apache License, Version 2.0 (the "License");
  2578. * you may not use this file except in compliance with the License.
  2579. * You may obtain a copy of the License at
  2580. *
  2581. * http://www.apache.org/licenses/LICENSE-2.0
  2582. *
  2583. * Unless required by applicable law or agreed to in writing, software
  2584. * distributed under the License is distributed on an "AS IS" BASIS,
  2585. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2586. * See the License for the specific language governing permissions and
  2587. * limitations under the License.
  2588. */
  2589. /**
  2590. * Class constructor for Tabs MDL component.
  2591. * Implements MDL component design pattern defined at:
  2592. * https://github.com/jasonmayes/mdl-component-design-pattern
  2593. *
  2594. * @constructor
  2595. * @param {Element} element The element that will be upgraded.
  2596. */
  2597. var MaterialTabs = function MaterialTabs(element) {
  2598. // Stores the HTML element.
  2599. this.element_ = element;
  2600. // Initialize instance.
  2601. this.init();
  2602. };
  2603. window['MaterialTabs'] = MaterialTabs;
  2604. /**
  2605. * Store constants in one place so they can be updated easily.
  2606. *
  2607. * @enum {string}
  2608. * @private
  2609. */
  2610. MaterialTabs.prototype.Constant_ = {};
  2611. /**
  2612. * Store strings for class names defined by this component that are used in
  2613. * JavaScript. This allows us to simply change it in one place should we
  2614. * decide to modify at a later date.
  2615. *
  2616. * @enum {string}
  2617. * @private
  2618. */
  2619. MaterialTabs.prototype.CssClasses_ = {
  2620. TAB_CLASS: 'mdl-tabs__tab',
  2621. PANEL_CLASS: 'mdl-tabs__panel',
  2622. ACTIVE_CLASS: 'is-active',
  2623. UPGRADED_CLASS: 'is-upgraded',
  2624. MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  2625. MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container',
  2626. MDL_RIPPLE: 'mdl-ripple',
  2627. MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events'
  2628. };
  2629. /**
  2630. * Handle clicks to a tabs component
  2631. *
  2632. * @private
  2633. */
  2634. MaterialTabs.prototype.initTabs_ = function () {
  2635. if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {
  2636. this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS);
  2637. }
  2638. // Select element tabs, document panels
  2639. this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS);
  2640. this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS);
  2641. // Create new tabs for each tab element
  2642. for (var i = 0; i < this.tabs_.length; i++) {
  2643. new MaterialTab(this.tabs_[i], this);
  2644. }
  2645. this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS);
  2646. };
  2647. /**
  2648. * Reset tab state, dropping active classes
  2649. *
  2650. * @private
  2651. */
  2652. MaterialTabs.prototype.resetTabState_ = function () {
  2653. for (var k = 0; k < this.tabs_.length; k++) {
  2654. this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS);
  2655. }
  2656. };
  2657. /**
  2658. * Reset panel state, droping active classes
  2659. *
  2660. * @private
  2661. */
  2662. MaterialTabs.prototype.resetPanelState_ = function () {
  2663. for (var j = 0; j < this.panels_.length; j++) {
  2664. this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS);
  2665. }
  2666. };
  2667. /**
  2668. * Initialize element.
  2669. */
  2670. MaterialTabs.prototype.init = function () {
  2671. if (this.element_) {
  2672. this.initTabs_();
  2673. }
  2674. };
  2675. /**
  2676. * Constructor for an individual tab.
  2677. *
  2678. * @constructor
  2679. * @param {Element} tab The HTML element for the tab.
  2680. * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab.
  2681. */
  2682. function MaterialTab(tab, ctx) {
  2683. if (tab) {
  2684. if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {
  2685. var rippleContainer = document.createElement('span');
  2686. rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER);
  2687. rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT);
  2688. var ripple = document.createElement('span');
  2689. ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE);
  2690. rippleContainer.appendChild(ripple);
  2691. tab.appendChild(rippleContainer);
  2692. }
  2693. tab.addEventListener('click', function (e) {
  2694. if (tab.getAttribute('href').charAt(0) === '#') {
  2695. e.preventDefault();
  2696. var href = tab.href.split('#')[1];
  2697. var panel = ctx.element_.querySelector('#' + href);
  2698. ctx.resetTabState_();
  2699. ctx.resetPanelState_();
  2700. tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS);
  2701. panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS);
  2702. }
  2703. });
  2704. }
  2705. }
  2706. // The component registers itself. It can assume componentHandler is available
  2707. // in the global scope.
  2708. componentHandler.register({
  2709. constructor: MaterialTabs,
  2710. classAsString: 'MaterialTabs',
  2711. cssClass: 'mdl-js-tabs'
  2712. });
  2713. /**
  2714. * @license
  2715. * Copyright 2015 Google Inc. All Rights Reserved.
  2716. *
  2717. * Licensed under the Apache License, Version 2.0 (the "License");
  2718. * you may not use this file except in compliance with the License.
  2719. * You may obtain a copy of the License at
  2720. *
  2721. * http://www.apache.org/licenses/LICENSE-2.0
  2722. *
  2723. * Unless required by applicable law or agreed to in writing, software
  2724. * distributed under the License is distributed on an "AS IS" BASIS,
  2725. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2726. * See the License for the specific language governing permissions and
  2727. * limitations under the License.
  2728. */
  2729. /**
  2730. * Class constructor for Textfield MDL component.
  2731. * Implements MDL component design pattern defined at:
  2732. * https://github.com/jasonmayes/mdl-component-design-pattern
  2733. *
  2734. * @constructor
  2735. * @param {HTMLElement} element The element that will be upgraded.
  2736. */
  2737. var MaterialTextfield = function MaterialTextfield(element) {
  2738. this.element_ = element;
  2739. this.maxRows = this.Constant_.NO_MAX_ROWS;
  2740. // Initialize instance.
  2741. this.init();
  2742. };
  2743. window['MaterialTextfield'] = MaterialTextfield;
  2744. /**
  2745. * Store constants in one place so they can be updated easily.
  2746. *
  2747. * @enum {string | number}
  2748. * @private
  2749. */
  2750. MaterialTextfield.prototype.Constant_ = {
  2751. NO_MAX_ROWS: -1,
  2752. MAX_ROWS_ATTRIBUTE: 'maxrows'
  2753. };
  2754. /**
  2755. * Store strings for class names defined by this component that are used in
  2756. * JavaScript. This allows us to simply change it in one place should we
  2757. * decide to modify at a later date.
  2758. *
  2759. * @enum {string}
  2760. * @private
  2761. */
  2762. MaterialTextfield.prototype.CssClasses_ = {
  2763. LABEL: 'mdl-textfield__label',
  2764. INPUT: 'mdl-textfield__input',
  2765. IS_DIRTY: 'is-dirty',
  2766. IS_FOCUSED: 'is-focused',
  2767. IS_DISABLED: 'is-disabled',
  2768. IS_INVALID: 'is-invalid',
  2769. IS_UPGRADED: 'is-upgraded',
  2770. HAS_PLACEHOLDER: 'has-placeholder'
  2771. };
  2772. /**
  2773. * Handle input being entered.
  2774. *
  2775. * @param {Event} event The event that fired.
  2776. * @private
  2777. */
  2778. MaterialTextfield.prototype.onKeyDown_ = function (event) {
  2779. var currentRowCount = event.target.value.split('\n').length;
  2780. if (event.keyCode === 13) {
  2781. if (currentRowCount >= this.maxRows) {
  2782. event.preventDefault();
  2783. }
  2784. }
  2785. };
  2786. /**
  2787. * Handle focus.
  2788. *
  2789. * @param {Event} event The event that fired.
  2790. * @private
  2791. */
  2792. MaterialTextfield.prototype.onFocus_ = function (event) {
  2793. this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  2794. };
  2795. /**
  2796. * Handle lost focus.
  2797. *
  2798. * @param {Event} event The event that fired.
  2799. * @private
  2800. */
  2801. MaterialTextfield.prototype.onBlur_ = function (event) {
  2802. this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  2803. };
  2804. /**
  2805. * Handle reset event from out side.
  2806. *
  2807. * @param {Event} event The event that fired.
  2808. * @private
  2809. */
  2810. MaterialTextfield.prototype.onReset_ = function (event) {
  2811. this.updateClasses_();
  2812. };
  2813. /**
  2814. * Handle class updates.
  2815. *
  2816. * @private
  2817. */
  2818. MaterialTextfield.prototype.updateClasses_ = function () {
  2819. this.checkDisabled();
  2820. this.checkValidity();
  2821. this.checkDirty();
  2822. this.checkFocus();
  2823. };
  2824. // Public methods.
  2825. /**
  2826. * Check the disabled state and update field accordingly.
  2827. *
  2828. * @public
  2829. */
  2830. MaterialTextfield.prototype.checkDisabled = function () {
  2831. if (this.input_.disabled) {
  2832. this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  2833. } else {
  2834. this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  2835. }
  2836. };
  2837. MaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled;
  2838. /**
  2839. * Check the focus state and update field accordingly.
  2840. *
  2841. * @public
  2842. */
  2843. MaterialTextfield.prototype.checkFocus = function () {
  2844. if (Boolean(this.element_.querySelector(':focus'))) {
  2845. this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  2846. } else {
  2847. this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  2848. }
  2849. };
  2850. MaterialTextfield.prototype['checkFocus'] = MaterialTextfield.prototype.checkFocus;
  2851. /**
  2852. * Check the validity state and update field accordingly.
  2853. *
  2854. * @public
  2855. */
  2856. MaterialTextfield.prototype.checkValidity = function () {
  2857. if (this.input_.validity) {
  2858. if (this.input_.validity.valid) {
  2859. this.element_.classList.remove(this.CssClasses_.IS_INVALID);
  2860. } else {
  2861. this.element_.classList.add(this.CssClasses_.IS_INVALID);
  2862. }
  2863. }
  2864. };
  2865. MaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity;
  2866. /**
  2867. * Check the dirty state and update field accordingly.
  2868. *
  2869. * @public
  2870. */
  2871. MaterialTextfield.prototype.checkDirty = function () {
  2872. if (this.input_.value && this.input_.value.length > 0) {
  2873. this.element_.classList.add(this.CssClasses_.IS_DIRTY);
  2874. } else {
  2875. this.element_.classList.remove(this.CssClasses_.IS_DIRTY);
  2876. }
  2877. };
  2878. MaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty;
  2879. /**
  2880. * Disable text field.
  2881. *
  2882. * @public
  2883. */
  2884. MaterialTextfield.prototype.disable = function () {
  2885. this.input_.disabled = true;
  2886. this.updateClasses_();
  2887. };
  2888. MaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable;
  2889. /**
  2890. * Enable text field.
  2891. *
  2892. * @public
  2893. */
  2894. MaterialTextfield.prototype.enable = function () {
  2895. this.input_.disabled = false;
  2896. this.updateClasses_();
  2897. };
  2898. MaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable;
  2899. /**
  2900. * Update text field value.
  2901. *
  2902. * @param {string} value The value to which to set the control (optional).
  2903. * @public
  2904. */
  2905. MaterialTextfield.prototype.change = function (value) {
  2906. this.input_.value = value || '';
  2907. this.updateClasses_();
  2908. };
  2909. MaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change;
  2910. /**
  2911. * Initialize element.
  2912. */
  2913. MaterialTextfield.prototype.init = function () {
  2914. if (this.element_) {
  2915. this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL);
  2916. this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
  2917. if (this.input_) {
  2918. if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) {
  2919. this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10);
  2920. if (isNaN(this.maxRows)) {
  2921. this.maxRows = this.Constant_.NO_MAX_ROWS;
  2922. }
  2923. }
  2924. if (this.input_.hasAttribute('placeholder')) {
  2925. this.element_.classList.add(this.CssClasses_.HAS_PLACEHOLDER);
  2926. }
  2927. this.boundUpdateClassesHandler = this.updateClasses_.bind(this);
  2928. this.boundFocusHandler = this.onFocus_.bind(this);
  2929. this.boundBlurHandler = this.onBlur_.bind(this);
  2930. this.boundResetHandler = this.onReset_.bind(this);
  2931. this.input_.addEventListener('input', this.boundUpdateClassesHandler);
  2932. this.input_.addEventListener('focus', this.boundFocusHandler);
  2933. this.input_.addEventListener('blur', this.boundBlurHandler);
  2934. this.input_.addEventListener('reset', this.boundResetHandler);
  2935. if (this.maxRows !== this.Constant_.NO_MAX_ROWS) {
  2936. // TODO: This should handle pasting multi line text.
  2937. // Currently doesn't.
  2938. this.boundKeyDownHandler = this.onKeyDown_.bind(this);
  2939. this.input_.addEventListener('keydown', this.boundKeyDownHandler);
  2940. }
  2941. var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID);
  2942. this.updateClasses_();
  2943. this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  2944. if (invalid) {
  2945. this.element_.classList.add(this.CssClasses_.IS_INVALID);
  2946. }
  2947. if (this.input_.hasAttribute('autofocus')) {
  2948. this.element_.focus();
  2949. this.checkFocus();
  2950. }
  2951. }
  2952. }
  2953. };
  2954. // The component registers itself. It can assume componentHandler is available
  2955. // in the global scope.
  2956. componentHandler.register({
  2957. constructor: MaterialTextfield,
  2958. classAsString: 'MaterialTextfield',
  2959. cssClass: 'mdl-js-textfield',
  2960. widget: true
  2961. });
  2962. /**
  2963. * @license
  2964. * Copyright 2015 Google Inc. All Rights Reserved.
  2965. *
  2966. * Licensed under the Apache License, Version 2.0 (the "License");
  2967. * you may not use this file except in compliance with the License.
  2968. * You may obtain a copy of the License at
  2969. *
  2970. * http://www.apache.org/licenses/LICENSE-2.0
  2971. *
  2972. * Unless required by applicable law or agreed to in writing, software
  2973. * distributed under the License is distributed on an "AS IS" BASIS,
  2974. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2975. * See the License for the specific language governing permissions and
  2976. * limitations under the License.
  2977. */
  2978. /**
  2979. * Class constructor for Tooltip MDL component.
  2980. * Implements MDL component design pattern defined at:
  2981. * https://github.com/jasonmayes/mdl-component-design-pattern
  2982. *
  2983. * @constructor
  2984. * @param {HTMLElement} element The element that will be upgraded.
  2985. */
  2986. var MaterialTooltip = function MaterialTooltip(element) {
  2987. this.element_ = element;
  2988. // Initialize instance.
  2989. this.init();
  2990. };
  2991. window['MaterialTooltip'] = MaterialTooltip;
  2992. /**
  2993. * Store constants in one place so they can be updated easily.
  2994. *
  2995. * @enum {string | number}
  2996. * @private
  2997. */
  2998. MaterialTooltip.prototype.Constant_ = {};
  2999. /**
  3000. * Store strings for class names defined by this component that are used in
  3001. * JavaScript. This allows us to simply change it in one place should we
  3002. * decide to modify at a later date.
  3003. *
  3004. * @enum {string}
  3005. * @private
  3006. */
  3007. MaterialTooltip.prototype.CssClasses_ = {
  3008. IS_ACTIVE: 'is-active',
  3009. BOTTOM: 'mdl-tooltip--bottom',
  3010. LEFT: 'mdl-tooltip--left',
  3011. RIGHT: 'mdl-tooltip--right',
  3012. TOP: 'mdl-tooltip--top'
  3013. };
  3014. /**
  3015. * Handle mouseenter for tooltip.
  3016. *
  3017. * @param {Event} event The event that fired.
  3018. * @private
  3019. */
  3020. MaterialTooltip.prototype.handleMouseEnter_ = function (event) {
  3021. var props = event.target.getBoundingClientRect();
  3022. var left = props.left + props.width / 2;
  3023. var top = props.top + props.height / 2;
  3024. var marginLeft = -1 * (this.element_.offsetWidth / 2);
  3025. var marginTop = -1 * (this.element_.offsetHeight / 2);
  3026. if (this.element_.classList.contains(this.CssClasses_.LEFT) || this.element_.classList.contains(this.CssClasses_.RIGHT)) {
  3027. left = props.width / 2;
  3028. if (top + marginTop < 0) {
  3029. this.element_.style.top = '0';
  3030. this.element_.style.marginTop = '0';
  3031. } else {
  3032. this.element_.style.top = top + 'px';
  3033. this.element_.style.marginTop = marginTop + 'px';
  3034. }
  3035. } else {
  3036. if (left + marginLeft < 0) {
  3037. this.element_.style.left = '0';
  3038. this.element_.style.marginLeft = '0';
  3039. } else {
  3040. this.element_.style.left = left + 'px';
  3041. this.element_.style.marginLeft = marginLeft + 'px';
  3042. }
  3043. }
  3044. if (this.element_.classList.contains(this.CssClasses_.TOP)) {
  3045. this.element_.style.top = props.top - this.element_.offsetHeight - 10 + 'px';
  3046. } else if (this.element_.classList.contains(this.CssClasses_.RIGHT)) {
  3047. this.element_.style.left = props.left + props.width + 10 + 'px';
  3048. } else if (this.element_.classList.contains(this.CssClasses_.LEFT)) {
  3049. this.element_.style.left = props.left - this.element_.offsetWidth - 10 + 'px';
  3050. } else {
  3051. this.element_.style.top = props.top + props.height + 10 + 'px';
  3052. }
  3053. this.element_.classList.add(this.CssClasses_.IS_ACTIVE);
  3054. };
  3055. /**
  3056. * Hide tooltip on mouseleave or scroll
  3057. *
  3058. * @private
  3059. */
  3060. MaterialTooltip.prototype.hideTooltip_ = function () {
  3061. this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);
  3062. };
  3063. /**
  3064. * Initialize element.
  3065. */
  3066. MaterialTooltip.prototype.init = function () {
  3067. if (this.element_) {
  3068. var forElId = this.element_.getAttribute('for') || this.element_.getAttribute('data-mdl-for');
  3069. if (forElId) {
  3070. this.forElement_ = document.getElementById(forElId);
  3071. }
  3072. if (this.forElement_) {
  3073. // It's left here because it prevents accidental text selection on Android
  3074. if (!this.forElement_.hasAttribute('tabindex')) {
  3075. this.forElement_.setAttribute('tabindex', '0');
  3076. }
  3077. this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);
  3078. this.boundMouseLeaveAndScrollHandler = this.hideTooltip_.bind(this);
  3079. this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);
  3080. this.forElement_.addEventListener('touchend', this.boundMouseEnterHandler, false);
  3081. this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveAndScrollHandler, false);
  3082. window.addEventListener('scroll', this.boundMouseLeaveAndScrollHandler, true);
  3083. window.addEventListener('touchstart', this.boundMouseLeaveAndScrollHandler);
  3084. }
  3085. }
  3086. };
  3087. // The component registers itself. It can assume componentHandler is available
  3088. // in the global scope.
  3089. componentHandler.register({
  3090. constructor: MaterialTooltip,
  3091. classAsString: 'MaterialTooltip',
  3092. cssClass: 'mdl-tooltip'
  3093. });
  3094. /**
  3095. * @license
  3096. * Copyright 2015 Google Inc. All Rights Reserved.
  3097. *
  3098. * Licensed under the Apache License, Version 2.0 (the "License");
  3099. * you may not use this file except in compliance with the License.
  3100. * You may obtain a copy of the License at
  3101. *
  3102. * http://www.apache.org/licenses/LICENSE-2.0
  3103. *
  3104. * Unless required by applicable law or agreed to in writing, software
  3105. * distributed under the License is distributed on an "AS IS" BASIS,
  3106. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3107. * See the License for the specific language governing permissions and
  3108. * limitations under the License.
  3109. */
  3110. /**
  3111. * Class constructor for Layout MDL component.
  3112. * Implements MDL component design pattern defined at:
  3113. * https://github.com/jasonmayes/mdl-component-design-pattern
  3114. *
  3115. * @constructor
  3116. * @param {HTMLElement} element The element that will be upgraded.
  3117. */
  3118. var MaterialLayout = function MaterialLayout(element) {
  3119. this.element_ = element;
  3120. // Initialize instance.
  3121. this.init();
  3122. };
  3123. window['MaterialLayout'] = MaterialLayout;
  3124. /**
  3125. * Store constants in one place so they can be updated easily.
  3126. *
  3127. * @enum {string | number}
  3128. * @private
  3129. */
  3130. MaterialLayout.prototype.Constant_ = {
  3131. MAX_WIDTH: '(max-width: 1024px)',
  3132. TAB_SCROLL_PIXELS: 100,
  3133. RESIZE_TIMEOUT: 100,
  3134. MENU_ICON: '&#xE5D2;',
  3135. CHEVRON_LEFT: 'chevron_left',
  3136. CHEVRON_RIGHT: 'chevron_right'
  3137. };
  3138. /**
  3139. * Keycodes, for code readability.
  3140. *
  3141. * @enum {number}
  3142. * @private
  3143. */
  3144. MaterialLayout.prototype.Keycodes_ = {
  3145. ENTER: 13,
  3146. ESCAPE: 27,
  3147. SPACE: 32
  3148. };
  3149. /**
  3150. * Modes.
  3151. *
  3152. * @enum {number}
  3153. * @private
  3154. */
  3155. MaterialLayout.prototype.Mode_ = {
  3156. STANDARD: 0,
  3157. SEAMED: 1,
  3158. WATERFALL: 2,
  3159. SCROLL: 3
  3160. };
  3161. /**
  3162. * Store strings for class names defined by this component that are used in
  3163. * JavaScript. This allows us to simply change it in one place should we
  3164. * decide to modify at a later date.
  3165. *
  3166. * @enum {string}
  3167. * @private
  3168. */
  3169. MaterialLayout.prototype.CssClasses_ = {
  3170. CONTAINER: 'mdl-layout__container',
  3171. HEADER: 'mdl-layout__header',
  3172. DRAWER: 'mdl-layout__drawer',
  3173. CONTENT: 'mdl-layout__content',
  3174. DRAWER_BTN: 'mdl-layout__drawer-button',
  3175. ICON: 'material-icons',
  3176. JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  3177. RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container',
  3178. RIPPLE: 'mdl-ripple',
  3179. RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  3180. HEADER_SEAMED: 'mdl-layout__header--seamed',
  3181. HEADER_WATERFALL: 'mdl-layout__header--waterfall',
  3182. HEADER_SCROLL: 'mdl-layout__header--scroll',
  3183. FIXED_HEADER: 'mdl-layout--fixed-header',
  3184. OBFUSCATOR: 'mdl-layout__obfuscator',
  3185. TAB_BAR: 'mdl-layout__tab-bar',
  3186. TAB_CONTAINER: 'mdl-layout__tab-bar-container',
  3187. TAB: 'mdl-layout__tab',
  3188. TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button',
  3189. TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button',
  3190. TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button',
  3191. TAB_MANUAL_SWITCH: 'mdl-layout__tab-manual-switch',
  3192. PANEL: 'mdl-layout__tab-panel',
  3193. HAS_DRAWER: 'has-drawer',
  3194. HAS_TABS: 'has-tabs',
  3195. HAS_SCROLLING_HEADER: 'has-scrolling-header',
  3196. CASTING_SHADOW: 'is-casting-shadow',
  3197. IS_COMPACT: 'is-compact',
  3198. IS_SMALL_SCREEN: 'is-small-screen',
  3199. IS_DRAWER_OPEN: 'is-visible',
  3200. IS_ACTIVE: 'is-active',
  3201. IS_UPGRADED: 'is-upgraded',
  3202. IS_ANIMATING: 'is-animating',
  3203. ON_LARGE_SCREEN: 'mdl-layout--large-screen-only',
  3204. ON_SMALL_SCREEN: 'mdl-layout--small-screen-only'
  3205. };
  3206. /**
  3207. * Handles scrolling on the content.
  3208. *
  3209. * @private
  3210. */
  3211. MaterialLayout.prototype.contentScrollHandler_ = function () {
  3212. if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) {
  3213. return;
  3214. }
  3215. var headerVisible = !this.element_.classList.contains(this.CssClasses_.IS_SMALL_SCREEN) || this.element_.classList.contains(this.CssClasses_.FIXED_HEADER);
  3216. if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {
  3217. this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);
  3218. this.header_.classList.add(this.CssClasses_.IS_COMPACT);
  3219. if (headerVisible) {
  3220. this.header_.classList.add(this.CssClasses_.IS_ANIMATING);
  3221. }
  3222. } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {
  3223. this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);
  3224. this.header_.classList.remove(this.CssClasses_.IS_COMPACT);
  3225. if (headerVisible) {
  3226. this.header_.classList.add(this.CssClasses_.IS_ANIMATING);
  3227. }
  3228. }
  3229. };
  3230. /**
  3231. * Handles a keyboard event on the drawer.
  3232. *
  3233. * @param {Event} evt The event that fired.
  3234. * @private
  3235. */
  3236. MaterialLayout.prototype.keyboardEventHandler_ = function (evt) {
  3237. // Only react when the drawer is open.
  3238. if (evt.keyCode === this.Keycodes_.ESCAPE && this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)) {
  3239. this.toggleDrawer();
  3240. }
  3241. };
  3242. /**
  3243. * Handles changes in screen size.
  3244. *
  3245. * @private
  3246. */
  3247. MaterialLayout.prototype.screenSizeHandler_ = function () {
  3248. if (this.screenSizeMediaQuery_.matches) {
  3249. this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN);
  3250. } else {
  3251. this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN);
  3252. // Collapse drawer (if any) when moving to a large screen size.
  3253. if (this.drawer_) {
  3254. this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);
  3255. this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);
  3256. }
  3257. }
  3258. };
  3259. /**
  3260. * Handles events of drawer button.
  3261. *
  3262. * @param {Event} evt The event that fired.
  3263. * @private
  3264. */
  3265. MaterialLayout.prototype.drawerToggleHandler_ = function (evt) {
  3266. if (evt && evt.type === 'keydown') {
  3267. if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {
  3268. // prevent scrolling in drawer nav
  3269. evt.preventDefault();
  3270. } else {
  3271. // prevent other keys
  3272. return;
  3273. }
  3274. }
  3275. this.toggleDrawer();
  3276. };
  3277. /**
  3278. * Handles (un)setting the `is-animating` class
  3279. *
  3280. * @private
  3281. */
  3282. MaterialLayout.prototype.headerTransitionEndHandler_ = function () {
  3283. this.header_.classList.remove(this.CssClasses_.IS_ANIMATING);
  3284. };
  3285. /**
  3286. * Handles expanding the header on click
  3287. *
  3288. * @private
  3289. */
  3290. MaterialLayout.prototype.headerClickHandler_ = function () {
  3291. if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {
  3292. this.header_.classList.remove(this.CssClasses_.IS_COMPACT);
  3293. this.header_.classList.add(this.CssClasses_.IS_ANIMATING);
  3294. }
  3295. };
  3296. /**
  3297. * Reset tab state, dropping active classes
  3298. *
  3299. * @private
  3300. */
  3301. MaterialLayout.prototype.resetTabState_ = function (tabBar) {
  3302. for (var k = 0; k < tabBar.length; k++) {
  3303. tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE);
  3304. }
  3305. };
  3306. /**
  3307. * Reset panel state, droping active classes
  3308. *
  3309. * @private
  3310. */
  3311. MaterialLayout.prototype.resetPanelState_ = function (panels) {
  3312. for (var j = 0; j < panels.length; j++) {
  3313. panels[j].classList.remove(this.CssClasses_.IS_ACTIVE);
  3314. }
  3315. };
  3316. /**
  3317. * Toggle drawer state
  3318. *
  3319. * @public
  3320. */
  3321. MaterialLayout.prototype.toggleDrawer = function () {
  3322. var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);
  3323. this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
  3324. this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
  3325. // Set accessibility properties.
  3326. if (this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)) {
  3327. this.drawer_.setAttribute('aria-hidden', 'false');
  3328. drawerButton.setAttribute('aria-expanded', 'true');
  3329. } else {
  3330. this.drawer_.setAttribute('aria-hidden', 'true');
  3331. drawerButton.setAttribute('aria-expanded', 'false');
  3332. }
  3333. };
  3334. MaterialLayout.prototype['toggleDrawer'] = MaterialLayout.prototype.toggleDrawer;
  3335. /**
  3336. * Initialize element.
  3337. */
  3338. MaterialLayout.prototype.init = function () {
  3339. if (this.element_) {
  3340. var container = document.createElement('div');
  3341. container.classList.add(this.CssClasses_.CONTAINER);
  3342. var focusedElement = this.element_.querySelector(':focus');
  3343. this.element_.parentElement.insertBefore(container, this.element_);
  3344. this.element_.parentElement.removeChild(this.element_);
  3345. container.appendChild(this.element_);
  3346. if (focusedElement) {
  3347. focusedElement.focus();
  3348. }
  3349. var directChildren = this.element_.childNodes;
  3350. var numChildren = directChildren.length;
  3351. for (var c = 0; c < numChildren; c++) {
  3352. var child = directChildren[c];
  3353. if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) {
  3354. this.header_ = child;
  3355. }
  3356. if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) {
  3357. this.drawer_ = child;
  3358. }
  3359. if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) {
  3360. this.content_ = child;
  3361. }
  3362. }
  3363. window.addEventListener('pageshow', function (e) {
  3364. if (e.persisted) {
  3365. // when page is loaded from back/forward cache
  3366. // trigger repaint to let layout scroll in safari
  3367. this.element_.style.overflowY = 'hidden';
  3368. requestAnimationFrame(function () {
  3369. this.element_.style.overflowY = '';
  3370. }.bind(this));
  3371. }
  3372. }.bind(this), false);
  3373. if (this.header_) {
  3374. this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR);
  3375. }
  3376. var mode = this.Mode_.STANDARD;
  3377. if (this.header_) {
  3378. if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) {
  3379. mode = this.Mode_.SEAMED;
  3380. } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) {
  3381. mode = this.Mode_.WATERFALL;
  3382. this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this));
  3383. this.header_.addEventListener('click', this.headerClickHandler_.bind(this));
  3384. } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) {
  3385. mode = this.Mode_.SCROLL;
  3386. container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER);
  3387. }
  3388. if (mode === this.Mode_.STANDARD) {
  3389. this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);
  3390. if (this.tabBar_) {
  3391. this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW);
  3392. }
  3393. } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) {
  3394. this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);
  3395. if (this.tabBar_) {
  3396. this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW);
  3397. }
  3398. } else if (mode === this.Mode_.WATERFALL) {
  3399. // Add and remove shadows depending on scroll position.
  3400. // Also add/remove auxiliary class for styling of the compact version of
  3401. // the header.
  3402. this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this));
  3403. this.contentScrollHandler_();
  3404. }
  3405. }
  3406. // Add drawer toggling button to our layout, if we have an openable drawer.
  3407. if (this.drawer_) {
  3408. var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);
  3409. if (!drawerButton) {
  3410. drawerButton = document.createElement('div');
  3411. drawerButton.setAttribute('aria-expanded', 'false');
  3412. drawerButton.setAttribute('role', 'button');
  3413. drawerButton.setAttribute('tabindex', '0');
  3414. drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);
  3415. var drawerButtonIcon = document.createElement('i');
  3416. drawerButtonIcon.classList.add(this.CssClasses_.ICON);
  3417. drawerButtonIcon.innerHTML = this.Constant_.MENU_ICON;
  3418. drawerButton.appendChild(drawerButtonIcon);
  3419. }
  3420. if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {
  3421. //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well.
  3422. drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN);
  3423. } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) {
  3424. //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well.
  3425. drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);
  3426. }
  3427. drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this));
  3428. drawerButton.addEventListener('keydown', this.drawerToggleHandler_.bind(this));
  3429. // Add a class if the layout has a drawer, for altering the left padding.
  3430. // Adds the HAS_DRAWER to the elements since this.header_ may or may
  3431. // not be present.
  3432. this.element_.classList.add(this.CssClasses_.HAS_DRAWER);
  3433. // If we have a fixed header, add the button to the header rather than
  3434. // the layout.
  3435. if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) {
  3436. this.header_.insertBefore(drawerButton, this.header_.firstChild);
  3437. } else {
  3438. this.element_.insertBefore(drawerButton, this.content_);
  3439. }
  3440. var obfuscator = document.createElement('div');
  3441. obfuscator.classList.add(this.CssClasses_.OBFUSCATOR);
  3442. this.element_.appendChild(obfuscator);
  3443. obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this));
  3444. this.obfuscator_ = obfuscator;
  3445. this.drawer_.addEventListener('keydown', this.keyboardEventHandler_.bind(this));
  3446. this.drawer_.setAttribute('aria-hidden', 'true');
  3447. }
  3448. // Keep an eye on screen size, and add/remove auxiliary class for styling
  3449. // of small screens.
  3450. this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH);
  3451. this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this));
  3452. this.screenSizeHandler_();
  3453. // Initialize tabs, if any.
  3454. if (this.header_ && this.tabBar_) {
  3455. this.element_.classList.add(this.CssClasses_.HAS_TABS);
  3456. var tabContainer = document.createElement('div');
  3457. tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER);
  3458. this.header_.insertBefore(tabContainer, this.tabBar_);
  3459. this.header_.removeChild(this.tabBar_);
  3460. var leftButton = document.createElement('div');
  3461. leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);
  3462. leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);
  3463. var leftButtonIcon = document.createElement('i');
  3464. leftButtonIcon.classList.add(this.CssClasses_.ICON);
  3465. leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT;
  3466. leftButton.appendChild(leftButtonIcon);
  3467. leftButton.addEventListener('click', function () {
  3468. this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS;
  3469. }.bind(this));
  3470. var rightButton = document.createElement('div');
  3471. rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);
  3472. rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);
  3473. var rightButtonIcon = document.createElement('i');
  3474. rightButtonIcon.classList.add(this.CssClasses_.ICON);
  3475. rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT;
  3476. rightButton.appendChild(rightButtonIcon);
  3477. rightButton.addEventListener('click', function () {
  3478. this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS;
  3479. }.bind(this));
  3480. tabContainer.appendChild(leftButton);
  3481. tabContainer.appendChild(this.tabBar_);
  3482. tabContainer.appendChild(rightButton);
  3483. // Add and remove tab buttons depending on scroll position and total
  3484. // window size.
  3485. var tabUpdateHandler = function () {
  3486. if (this.tabBar_.scrollLeft > 0) {
  3487. leftButton.classList.add(this.CssClasses_.IS_ACTIVE);
  3488. } else {
  3489. leftButton.classList.remove(this.CssClasses_.IS_ACTIVE);
  3490. }
  3491. if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) {
  3492. rightButton.classList.add(this.CssClasses_.IS_ACTIVE);
  3493. } else {
  3494. rightButton.classList.remove(this.CssClasses_.IS_ACTIVE);
  3495. }
  3496. }.bind(this);
  3497. this.tabBar_.addEventListener('scroll', tabUpdateHandler);
  3498. tabUpdateHandler();
  3499. // Update tabs when the window resizes.
  3500. var windowResizeHandler = function () {
  3501. // Use timeouts to make sure it doesn't happen too often.
  3502. if (this.resizeTimeoutId_) {
  3503. clearTimeout(this.resizeTimeoutId_);
  3504. }
  3505. this.resizeTimeoutId_ = setTimeout(function () {
  3506. tabUpdateHandler();
  3507. this.resizeTimeoutId_ = null;
  3508. }.bind(this), this.Constant_.RESIZE_TIMEOUT);
  3509. }.bind(this);
  3510. window.addEventListener('resize', windowResizeHandler);
  3511. if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {
  3512. this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  3513. }
  3514. // Select element tabs, document panels
  3515. var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB);
  3516. var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL);
  3517. // Create new tabs for each tab element
  3518. for (var i = 0; i < tabs.length; i++) {
  3519. new MaterialLayoutTab(tabs[i], tabs, panels, this);
  3520. }
  3521. }
  3522. this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  3523. }
  3524. };
  3525. /**
  3526. * Constructor for an individual tab.
  3527. *
  3528. * @constructor
  3529. * @param {HTMLElement} tab The HTML element for the tab.
  3530. * @param {!Array<HTMLElement>} tabs Array with HTML elements for all tabs.
  3531. * @param {!Array<HTMLElement>} panels Array with HTML elements for all panels.
  3532. * @param {MaterialLayout} layout The MaterialLayout object that owns the tab.
  3533. */
  3534. function MaterialLayoutTab(tab, tabs, panels, layout) {
  3535. /**
  3536. * Auxiliary method to programmatically select a tab in the UI.
  3537. */
  3538. function selectTab() {
  3539. var href = tab.href.split('#')[1];
  3540. var panel = layout.content_.querySelector('#' + href);
  3541. layout.resetTabState_(tabs);
  3542. layout.resetPanelState_(panels);
  3543. tab.classList.add(layout.CssClasses_.IS_ACTIVE);
  3544. panel.classList.add(layout.CssClasses_.IS_ACTIVE);
  3545. }
  3546. if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) {
  3547. var rippleContainer = document.createElement('span');
  3548. rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER);
  3549. rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT);
  3550. var ripple = document.createElement('span');
  3551. ripple.classList.add(layout.CssClasses_.RIPPLE);
  3552. rippleContainer.appendChild(ripple);
  3553. tab.appendChild(rippleContainer);
  3554. }
  3555. if (!layout.tabBar_.classList.contains(layout.CssClasses_.TAB_MANUAL_SWITCH)) {
  3556. tab.addEventListener('click', function (e) {
  3557. if (tab.getAttribute('href').charAt(0) === '#') {
  3558. e.preventDefault();
  3559. selectTab();
  3560. }
  3561. });
  3562. }
  3563. tab.show = selectTab;
  3564. }
  3565. window['MaterialLayoutTab'] = MaterialLayoutTab;
  3566. // The component registers itself. It can assume componentHandler is available
  3567. // in the global scope.
  3568. componentHandler.register({
  3569. constructor: MaterialLayout,
  3570. classAsString: 'MaterialLayout',
  3571. cssClass: 'mdl-js-layout'
  3572. });
  3573. /**
  3574. * @license
  3575. * Copyright 2015 Google Inc. All Rights Reserved.
  3576. *
  3577. * Licensed under the Apache License, Version 2.0 (the "License");
  3578. * you may not use this file except in compliance with the License.
  3579. * You may obtain a copy of the License at
  3580. *
  3581. * http://www.apache.org/licenses/LICENSE-2.0
  3582. *
  3583. * Unless required by applicable law or agreed to in writing, software
  3584. * distributed under the License is distributed on an "AS IS" BASIS,
  3585. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3586. * See the License for the specific language governing permissions and
  3587. * limitations under the License.
  3588. */
  3589. /**
  3590. * Class constructor for Data Table Card MDL component.
  3591. * Implements MDL component design pattern defined at:
  3592. * https://github.com/jasonmayes/mdl-component-design-pattern
  3593. *
  3594. * @constructor
  3595. * @param {Element} element The element that will be upgraded.
  3596. */
  3597. var MaterialDataTable = function MaterialDataTable(element) {
  3598. this.element_ = element;
  3599. // Initialize instance.
  3600. this.init();
  3601. };
  3602. window['MaterialDataTable'] = MaterialDataTable;
  3603. /**
  3604. * Store constants in one place so they can be updated easily.
  3605. *
  3606. * @enum {string | number}
  3607. * @private
  3608. */
  3609. MaterialDataTable.prototype.Constant_ = {};
  3610. /**
  3611. * Store strings for class names defined by this component that are used in
  3612. * JavaScript. This allows us to simply change it in one place should we
  3613. * decide to modify at a later date.
  3614. *
  3615. * @enum {string}
  3616. * @private
  3617. */
  3618. MaterialDataTable.prototype.CssClasses_ = {
  3619. DATA_TABLE: 'mdl-data-table',
  3620. SELECTABLE: 'mdl-data-table--selectable',
  3621. SELECT_ELEMENT: 'mdl-data-table__select',
  3622. IS_SELECTED: 'is-selected',
  3623. IS_UPGRADED: 'is-upgraded'
  3624. };
  3625. /**
  3626. * Generates and returns a function that toggles the selection state of a
  3627. * single row (or multiple rows).
  3628. *
  3629. * @param {Element} checkbox Checkbox that toggles the selection state.
  3630. * @param {Element} row Row to toggle when checkbox changes.
  3631. * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes.
  3632. * @private
  3633. */
  3634. MaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) {
  3635. if (row) {
  3636. return function () {
  3637. if (checkbox.checked) {
  3638. row.classList.add(this.CssClasses_.IS_SELECTED);
  3639. } else {
  3640. row.classList.remove(this.CssClasses_.IS_SELECTED);
  3641. }
  3642. }.bind(this);
  3643. }
  3644. if (opt_rows) {
  3645. return function () {
  3646. var i;
  3647. var el;
  3648. if (checkbox.checked) {
  3649. for (i = 0; i < opt_rows.length; i++) {
  3650. el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');
  3651. el['MaterialCheckbox'].check();
  3652. opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED);
  3653. }
  3654. } else {
  3655. for (i = 0; i < opt_rows.length; i++) {
  3656. el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');
  3657. el['MaterialCheckbox'].uncheck();
  3658. opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED);
  3659. }
  3660. }
  3661. }.bind(this);
  3662. }
  3663. };
  3664. /**
  3665. * Creates a checkbox for a single or or multiple rows and hooks up the
  3666. * event handling.
  3667. *
  3668. * @param {Element} row Row to toggle when checkbox changes.
  3669. * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes.
  3670. * @private
  3671. */
  3672. MaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {
  3673. var label = document.createElement('label');
  3674. var labelClasses = [
  3675. 'mdl-checkbox',
  3676. 'mdl-js-checkbox',
  3677. 'mdl-js-ripple-effect',
  3678. this.CssClasses_.SELECT_ELEMENT
  3679. ];
  3680. label.className = labelClasses.join(' ');
  3681. var checkbox = document.createElement('input');
  3682. checkbox.type = 'checkbox';
  3683. checkbox.classList.add('mdl-checkbox__input');
  3684. if (row) {
  3685. checkbox.checked = row.classList.contains(this.CssClasses_.IS_SELECTED);
  3686. checkbox.addEventListener('change', this.selectRow_(checkbox, row));
  3687. } else if (opt_rows) {
  3688. checkbox.addEventListener('change', this.selectRow_(checkbox, null, opt_rows));
  3689. }
  3690. label.appendChild(checkbox);
  3691. componentHandler.upgradeElement(label, 'MaterialCheckbox');
  3692. return label;
  3693. };
  3694. /**
  3695. * Initialize element.
  3696. */
  3697. MaterialDataTable.prototype.init = function () {
  3698. if (this.element_) {
  3699. var firstHeader = this.element_.querySelector('th');
  3700. var bodyRows = Array.prototype.slice.call(this.element_.querySelectorAll('tbody tr'));
  3701. var footRows = Array.prototype.slice.call(this.element_.querySelectorAll('tfoot tr'));
  3702. var rows = bodyRows.concat(footRows);
  3703. if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) {
  3704. var th = document.createElement('th');
  3705. var headerCheckbox = this.createCheckbox_(null, rows);
  3706. th.appendChild(headerCheckbox);
  3707. firstHeader.parentElement.insertBefore(th, firstHeader);
  3708. for (var i = 0; i < rows.length; i++) {
  3709. var firstCell = rows[i].querySelector('td');
  3710. if (firstCell) {
  3711. var td = document.createElement('td');
  3712. if (rows[i].parentNode.nodeName.toUpperCase() === 'TBODY') {
  3713. var rowCheckbox = this.createCheckbox_(rows[i]);
  3714. td.appendChild(rowCheckbox);
  3715. }
  3716. rows[i].insertBefore(td, firstCell);
  3717. }
  3718. }
  3719. this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  3720. }
  3721. }
  3722. };
  3723. // The component registers itself. It can assume componentHandler is available
  3724. // in the global scope.
  3725. componentHandler.register({
  3726. constructor: MaterialDataTable,
  3727. classAsString: 'MaterialDataTable',
  3728. cssClass: 'mdl-js-data-table'
  3729. });
  3730. /**
  3731. * @license
  3732. * Copyright 2015 Google Inc. All Rights Reserved.
  3733. *
  3734. * Licensed under the Apache License, Version 2.0 (the "License");
  3735. * you may not use this file except in compliance with the License.
  3736. * You may obtain a copy of the License at
  3737. *
  3738. * http://www.apache.org/licenses/LICENSE-2.0
  3739. *
  3740. * Unless required by applicable law or agreed to in writing, software
  3741. * distributed under the License is distributed on an "AS IS" BASIS,
  3742. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3743. * See the License for the specific language governing permissions and
  3744. * limitations under the License.
  3745. */
  3746. /**
  3747. * Class constructor for Ripple MDL component.
  3748. * Implements MDL component design pattern defined at:
  3749. * https://github.com/jasonmayes/mdl-component-design-pattern
  3750. *
  3751. * @constructor
  3752. * @param {HTMLElement} element The element that will be upgraded.
  3753. */
  3754. var MaterialRipple = function MaterialRipple(element) {
  3755. this.element_ = element;
  3756. // Initialize instance.
  3757. this.init();
  3758. };
  3759. window['MaterialRipple'] = MaterialRipple;
  3760. /**
  3761. * Store constants in one place so they can be updated easily.
  3762. *
  3763. * @enum {string | number}
  3764. * @private
  3765. */
  3766. MaterialRipple.prototype.Constant_ = {
  3767. INITIAL_SCALE: 'scale(0.0001, 0.0001)',
  3768. INITIAL_SIZE: '1px',
  3769. INITIAL_OPACITY: '0.4',
  3770. FINAL_OPACITY: '0',
  3771. FINAL_SCALE: ''
  3772. };
  3773. /**
  3774. * Store strings for class names defined by this component that are used in
  3775. * JavaScript. This allows us to simply change it in one place should we
  3776. * decide to modify at a later date.
  3777. *
  3778. * @enum {string}
  3779. * @private
  3780. */
  3781. MaterialRipple.prototype.CssClasses_ = {
  3782. RIPPLE_CENTER: 'mdl-ripple--center',
  3783. RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  3784. RIPPLE: 'mdl-ripple',
  3785. IS_ANIMATING: 'is-animating',
  3786. IS_VISIBLE: 'is-visible'
  3787. };
  3788. /**
  3789. * Handle mouse / finger down on element.
  3790. *
  3791. * @param {Event} event The event that fired.
  3792. * @private
  3793. */
  3794. MaterialRipple.prototype.downHandler_ = function (event) {
  3795. if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {
  3796. var rect = this.element_.getBoundingClientRect();
  3797. this.boundHeight = rect.height;
  3798. this.boundWidth = rect.width;
  3799. this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2;
  3800. this.rippleElement_.style.width = this.rippleSize_ + 'px';
  3801. this.rippleElement_.style.height = this.rippleSize_ + 'px';
  3802. }
  3803. this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);
  3804. if (event.type === 'mousedown' && this.ignoringMouseDown_) {
  3805. this.ignoringMouseDown_ = false;
  3806. } else {
  3807. if (event.type === 'touchstart') {
  3808. this.ignoringMouseDown_ = true;
  3809. }
  3810. var frameCount = this.getFrameCount();
  3811. if (frameCount > 0) {
  3812. return;
  3813. }
  3814. this.setFrameCount(1);
  3815. var bound = event.currentTarget.getBoundingClientRect();
  3816. var x;
  3817. var y;
  3818. // Check if we are handling a keyboard click.
  3819. if (event.clientX === 0 && event.clientY === 0) {
  3820. x = Math.round(bound.width / 2);
  3821. y = Math.round(bound.height / 2);
  3822. } else {
  3823. var clientX = event.clientX !== undefined ? event.clientX : event.touches[0].clientX;
  3824. var clientY = event.clientY !== undefined ? event.clientY : event.touches[0].clientY;
  3825. x = Math.round(clientX - bound.left);
  3826. y = Math.round(clientY - bound.top);
  3827. }
  3828. this.setRippleXY(x, y);
  3829. this.setRippleStyles(true);
  3830. window.requestAnimationFrame(this.animFrameHandler.bind(this));
  3831. }
  3832. };
  3833. /**
  3834. * Handle mouse / finger up on element.
  3835. *
  3836. * @param {Event} event The event that fired.
  3837. * @private
  3838. */
  3839. MaterialRipple.prototype.upHandler_ = function (event) {
  3840. // Don't fire for the artificial "mouseup" generated by a double-click.
  3841. if (event && event.detail !== 2) {
  3842. // Allow a repaint to occur before removing this class, so the animation
  3843. // shows for tap events, which seem to trigger a mouseup too soon after
  3844. // mousedown.
  3845. window.setTimeout(function () {
  3846. this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
  3847. }.bind(this), 0);
  3848. }
  3849. };
  3850. /**
  3851. * Initialize element.
  3852. */
  3853. MaterialRipple.prototype.init = function () {
  3854. if (this.element_) {
  3855. var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);
  3856. if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {
  3857. this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE);
  3858. this.frameCount_ = 0;
  3859. this.rippleSize_ = 0;
  3860. this.x_ = 0;
  3861. this.y_ = 0;
  3862. // Touch start produces a compat mouse down event, which would cause a
  3863. // second ripples. To avoid that, we use this property to ignore the first
  3864. // mouse down after a touch start.
  3865. this.ignoringMouseDown_ = false;
  3866. this.boundDownHandler = this.downHandler_.bind(this);
  3867. this.element_.addEventListener('mousedown', this.boundDownHandler);
  3868. this.element_.addEventListener('touchstart', this.boundDownHandler);
  3869. this.boundUpHandler = this.upHandler_.bind(this);
  3870. this.element_.addEventListener('mouseup', this.boundUpHandler);
  3871. this.element_.addEventListener('mouseleave', this.boundUpHandler);
  3872. this.element_.addEventListener('touchend', this.boundUpHandler);
  3873. this.element_.addEventListener('blur', this.boundUpHandler);
  3874. /**
  3875. * Getter for frameCount_.
  3876. * @return {number} the frame count.
  3877. */
  3878. this.getFrameCount = function () {
  3879. return this.frameCount_;
  3880. };
  3881. /**
  3882. * Setter for frameCount_.
  3883. * @param {number} fC the frame count.
  3884. */
  3885. this.setFrameCount = function (fC) {
  3886. this.frameCount_ = fC;
  3887. };
  3888. /**
  3889. * Getter for rippleElement_.
  3890. * @return {Element} the ripple element.
  3891. */
  3892. this.getRippleElement = function () {
  3893. return this.rippleElement_;
  3894. };
  3895. /**
  3896. * Sets the ripple X and Y coordinates.
  3897. * @param {number} newX the new X coordinate
  3898. * @param {number} newY the new Y coordinate
  3899. */
  3900. this.setRippleXY = function (newX, newY) {
  3901. this.x_ = newX;
  3902. this.y_ = newY;
  3903. };
  3904. /**
  3905. * Sets the ripple styles.
  3906. * @param {boolean} start whether or not this is the start frame.
  3907. */
  3908. this.setRippleStyles = function (start) {
  3909. if (this.rippleElement_ !== null) {
  3910. var transformString;
  3911. var scale;
  3912. var size;
  3913. var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';
  3914. if (start) {
  3915. scale = this.Constant_.INITIAL_SCALE;
  3916. size = this.Constant_.INITIAL_SIZE;
  3917. } else {
  3918. scale = this.Constant_.FINAL_SCALE;
  3919. size = this.rippleSize_ + 'px';
  3920. if (recentering) {
  3921. offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)';
  3922. }
  3923. }
  3924. transformString = 'translate(-50%, -50%) ' + offset + scale;
  3925. this.rippleElement_.style.webkitTransform = transformString;
  3926. this.rippleElement_.style.msTransform = transformString;
  3927. this.rippleElement_.style.transform = transformString;
  3928. if (start) {
  3929. this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);
  3930. } else {
  3931. this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);
  3932. }
  3933. }
  3934. };
  3935. /**
  3936. * Handles an animation frame.
  3937. */
  3938. this.animFrameHandler = function () {
  3939. if (this.frameCount_-- > 0) {
  3940. window.requestAnimationFrame(this.animFrameHandler.bind(this));
  3941. } else {
  3942. this.setRippleStyles(false);
  3943. }
  3944. };
  3945. }
  3946. }
  3947. };
  3948. // The component registers itself. It can assume componentHandler is available
  3949. // in the global scope.
  3950. componentHandler.register({
  3951. constructor: MaterialRipple,
  3952. classAsString: 'MaterialRipple',
  3953. cssClass: 'mdl-js-ripple-effect',
  3954. widget: false
  3955. });
  3956. }());