bootstrap-native.js 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032
  1. // Native Javascript for Bootstrap 3 v2.0.27 | © dnp_theme | MIT-License
  2. (function (root, factory) {
  3. if (typeof define === 'function' && define.amd) {
  4. // AMD support:
  5. define([], factory);
  6. } else if (typeof module === 'object' && module.exports) {
  7. // CommonJS-like:
  8. module.exports = factory();
  9. } else {
  10. // Browser globals (root is window)
  11. var bsn = factory();
  12. root.Affix = bsn.Affix;
  13. root.Alert = bsn.Alert;
  14. root.Button = bsn.Button;
  15. root.Carousel = bsn.Carousel;
  16. root.Collapse = bsn.Collapse;
  17. root.Dropdown = bsn.Dropdown;
  18. root.Modal = bsn.Modal;
  19. root.Popover = bsn.Popover;
  20. root.ScrollSpy = bsn.ScrollSpy;
  21. root.Tab = bsn.Tab;
  22. root.Tooltip = bsn.Tooltip;
  23. }
  24. }(this, function () {
  25. /* Native Javascript for Bootstrap 3 | Internal Utility Functions
  26. ----------------------------------------------------------------*/
  27. "use strict";
  28. // globals
  29. var globalObject = typeof global !== 'undefined' ? global : this||window,
  30. DOC = document, HTML = DOC.documentElement, body = 'body', // allow the library to be used in <head>
  31. // Native Javascript for Bootstrap Global Object
  32. BSN = globalObject.BSN = {},
  33. supports = BSN.supports = [],
  34. // function toggle attributes
  35. dataToggle = 'data-toggle',
  36. dataDismiss = 'data-dismiss',
  37. dataSpy = 'data-spy',
  38. dataRide = 'data-ride',
  39. // components
  40. stringAffix = 'Affix',
  41. stringAlert = 'Alert',
  42. stringButton = 'Button',
  43. stringCarousel = 'Carousel',
  44. stringCollapse = 'Collapse',
  45. stringDropdown = 'Dropdown',
  46. stringModal = 'Modal',
  47. stringPopover = 'Popover',
  48. stringScrollSpy = 'ScrollSpy',
  49. stringTab = 'Tab',
  50. stringTooltip = 'Tooltip',
  51. // options DATA API
  52. databackdrop = 'data-backdrop',
  53. dataKeyboard = 'data-keyboard',
  54. dataTarget = 'data-target',
  55. dataInterval = 'data-interval',
  56. dataHeight = 'data-height',
  57. dataPause = 'data-pause',
  58. dataTitle = 'data-title',
  59. dataOriginalTitle = 'data-original-title',
  60. dataOriginalText = 'data-original-text',
  61. dataDismissible = 'data-dismissible',
  62. dataTrigger = 'data-trigger',
  63. dataAnimation = 'data-animation',
  64. dataContainer = 'data-container',
  65. dataPlacement = 'data-placement',
  66. dataDelay = 'data-delay',
  67. dataOffsetTop = 'data-offset-top',
  68. dataOffsetBottom = 'data-offset-bottom',
  69. // option keys
  70. backdrop = 'backdrop', keyboard = 'keyboard', delay = 'delay',
  71. content = 'content', target = 'target', currentTarget = 'currentTarget',
  72. interval = 'interval', pause = 'pause', animation = 'animation',
  73. placement = 'placement', container = 'container',
  74. // box model
  75. offsetTop = 'offsetTop', offsetBottom = 'offsetBottom',
  76. offsetLeft = 'offsetLeft',
  77. scrollTop = 'scrollTop', scrollLeft = 'scrollLeft',
  78. clientWidth = 'clientWidth', clientHeight = 'clientHeight',
  79. offsetWidth = 'offsetWidth', offsetHeight = 'offsetHeight',
  80. innerWidth = 'innerWidth', innerHeight = 'innerHeight',
  81. scrollHeight = 'scrollHeight', height = 'height',
  82. // aria
  83. ariaExpanded = 'aria-expanded',
  84. ariaHidden = 'aria-hidden',
  85. // event names
  86. clickEvent = 'click',
  87. focusEvent = 'focus',
  88. hoverEvent = 'hover',
  89. keydownEvent = 'keydown',
  90. keyupEvent = 'keyup',
  91. resizeEvent = 'resize', // passive
  92. scrollEvent = 'scroll', // passive
  93. mouseHover = ('onmouseleave' in DOC) ? [ 'mouseenter', 'mouseleave'] : [ 'mouseover', 'mouseout' ],
  94. // touch since 2.0.26
  95. touchEvents = { start: 'touchstart', end: 'touchend', move:'touchmove' }, // passive
  96. // originalEvents
  97. showEvent = 'show',
  98. shownEvent = 'shown',
  99. hideEvent = 'hide',
  100. hiddenEvent = 'hidden',
  101. closeEvent = 'close',
  102. closedEvent = 'closed',
  103. slidEvent = 'slid',
  104. slideEvent = 'slide',
  105. changeEvent = 'change',
  106. // other
  107. getAttribute = 'getAttribute',
  108. setAttribute = 'setAttribute',
  109. hasAttribute = 'hasAttribute',
  110. createElement = 'createElement',
  111. appendChild = 'appendChild',
  112. innerHTML = 'innerHTML',
  113. getElementsByTagName = 'getElementsByTagName',
  114. preventDefault = 'preventDefault',
  115. getBoundingClientRect = 'getBoundingClientRect',
  116. querySelectorAll = 'querySelectorAll',
  117. getElementsByCLASSNAME = 'getElementsByClassName',
  118. getComputedStyle = 'getComputedStyle',
  119. indexOf = 'indexOf',
  120. parentNode = 'parentNode',
  121. length = 'length',
  122. toLowerCase = 'toLowerCase',
  123. Transition = 'Transition',
  124. Duration = 'Duration',
  125. Webkit = 'Webkit',
  126. style = 'style',
  127. push = 'push',
  128. tabindex = 'tabindex',
  129. contains = 'contains',
  130. active = 'active',
  131. inClass = 'in',
  132. collapsing = 'collapsing',
  133. disabled = 'disabled',
  134. loading = 'loading',
  135. left = 'left',
  136. right = 'right',
  137. top = 'top',
  138. bottom = 'bottom',
  139. // IE8 browser detect
  140. isIE8 = !('opacity' in HTML[style]),
  141. // tooltip / popover
  142. tipPositions = /\b(top|bottom|left|right)+/,
  143. // modal
  144. modalOverlay = 0,
  145. fixedTop = 'navbar-fixed-top',
  146. fixedBottom = 'navbar-fixed-bottom',
  147. // transitionEnd since 2.0.4
  148. supportTransitions = Webkit+Transition in HTML[style] || Transition[toLowerCase]() in HTML[style],
  149. transitionEndEvent = Webkit+Transition in HTML[style] ? Webkit[toLowerCase]()+Transition+'End' : Transition[toLowerCase]()+'end',
  150. transitionDuration = Webkit+Duration in HTML[style] ? Webkit[toLowerCase]()+Transition+Duration : Transition[toLowerCase]()+Duration,
  151. // set new focus element since 2.0.3
  152. setFocus = function(element){
  153. element.focus ? element.focus() : element.setActive();
  154. },
  155. // class manipulation, since 2.0.0 requires polyfill.js
  156. addClass = function(element,classNAME) {
  157. element.classList.add(classNAME);
  158. },
  159. removeClass = function(element,classNAME) {
  160. element.classList.remove(classNAME);
  161. },
  162. hasClass = function(element,classNAME){ // since 2.0.0
  163. return element.classList[contains](classNAME);
  164. },
  165. // selection methods
  166. nodeListToArray = function(nodeList){
  167. var childItems = []; for (var i = 0, nll = nodeList[length]; i<nll; i++) { childItems[push]( nodeList[i] ) }
  168. return childItems;
  169. },
  170. getElementsByClassName = function(element,classNAME) { // getElementsByClassName IE8+
  171. var selectionMethod = isIE8 ? querySelectorAll : getElementsByCLASSNAME;
  172. return nodeListToArray(element[selectionMethod]( isIE8 ? '.' + classNAME.replace(/\s(?=[a-z])/g,'.') : classNAME ));
  173. },
  174. queryElement = function (selector, parent) {
  175. var lookUp = parent ? parent : DOC;
  176. return typeof selector === 'object' ? selector : lookUp.querySelector(selector);
  177. },
  178. getClosest = function (element, selector) { //element is the element and selector is for the closest parent element to find
  179. // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
  180. var firstChar = selector.charAt(0), selectorSubstring = selector.substr(1);
  181. if ( firstChar === '.' ) {// If selector is a class
  182. for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match
  183. if ( queryElement(selector,element[parentNode]) !== null && hasClass(element,selectorSubstring) ) { return element; }
  184. }
  185. } else if ( firstChar === '#' ) { // If selector is an ID
  186. for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match
  187. if ( element.id === selectorSubstring ) { return element; }
  188. }
  189. }
  190. return false;
  191. },
  192. // event attach jQuery style / trigger since 1.2.0
  193. on = function (element, event, handler, options) {
  194. options = options || false;
  195. element.addEventListener(event, handler, options);
  196. },
  197. off = function(element, event, handler, options) {
  198. options = options || false;
  199. element.removeEventListener(event, handler, options);
  200. },
  201. one = function (element, event, handler, options) { // one since 2.0.4
  202. on(element, event, function handlerWrapper(e){
  203. handler(e);
  204. off(element, event, handlerWrapper, options);
  205. }, options);
  206. },
  207. // determine support for passive events
  208. supportPassive = (function(){
  209. // Test via a getter in the options object to see if the passive property is accessed
  210. var result = false;
  211. try {
  212. var opts = Object.defineProperty({}, 'passive', {
  213. get: function() {
  214. result = true;
  215. }
  216. });
  217. one(globalObject, 'testPassive', null, opts);
  218. } catch (e) {}
  219. return result;
  220. }()),
  221. // event options
  222. // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
  223. passiveHandler = supportPassive ? { passive: true } : false,
  224. // transitions
  225. getTransitionDurationFromElement = function(element) {
  226. var duration = supportTransitions ? globalObject[getComputedStyle](element)[transitionDuration] : 0;
  227. duration = parseFloat(duration);
  228. duration = typeof duration === 'number' && !isNaN(duration) ? duration * 1000 : 0;
  229. return duration; // we take a short offset to make sure we fire on the next frame after animation
  230. },
  231. emulateTransitionEnd = function(element,handler){ // emulateTransitionEnd since 2.0.4
  232. var called = 0, duration = getTransitionDurationFromElement(element);
  233. duration ? one(element, transitionEndEvent, function(e){ !called && handler(e), called = 1; })
  234. : setTimeout(function() { !called && handler(), called = 1; }, 17);
  235. },
  236. bootstrapCustomEvent = function (eventName, componentName, related) {
  237. var OriginalCustomEvent = new CustomEvent( eventName + '.bs.' + componentName);
  238. OriginalCustomEvent.relatedTarget = related;
  239. this.dispatchEvent(OriginalCustomEvent);
  240. },
  241. // tooltip / popover stuff
  242. getScroll = function() { // also Affix and ScrollSpy uses it
  243. return {
  244. y : globalObject.pageYOffset || HTML[scrollTop],
  245. x : globalObject.pageXOffset || HTML[scrollLeft]
  246. }
  247. },
  248. styleTip = function(link,element,position,parent) { // both popovers and tooltips (target,tooltip/popover,placement,elementToAppendTo)
  249. var elementDimensions = { w : element[offsetWidth], h: element[offsetHeight] },
  250. windowWidth = (HTML[clientWidth] || DOC[body][clientWidth]),
  251. windowHeight = (HTML[clientHeight] || DOC[body][clientHeight]),
  252. rect = link[getBoundingClientRect](),
  253. scroll = parent === DOC[body] ? getScroll() : { x: parent[offsetLeft] + parent[scrollLeft], y: parent[offsetTop] + parent[scrollTop] },
  254. linkDimensions = { w: rect[right] - rect[left], h: rect[bottom] - rect[top] },
  255. arrow = queryElement('[class*="arrow"]',element),
  256. topPosition, leftPosition, arrowTop, arrowLeft,
  257. halfTopExceed = rect[top] + linkDimensions.h/2 - elementDimensions.h/2 < 0,
  258. halfLeftExceed = rect[left] + linkDimensions.w/2 - elementDimensions.w/2 < 0,
  259. halfRightExceed = rect[left] + elementDimensions.w/2 + linkDimensions.w/2 >= windowWidth,
  260. halfBottomExceed = rect[top] + elementDimensions.h/2 + linkDimensions.h/2 >= windowHeight,
  261. topExceed = rect[top] - elementDimensions.h < 0,
  262. leftExceed = rect[left] - elementDimensions.w < 0,
  263. bottomExceed = rect[top] + elementDimensions.h + linkDimensions.h >= windowHeight,
  264. rightExceed = rect[left] + elementDimensions.w + linkDimensions.w >= windowWidth;
  265. // recompute position
  266. position = (position === left || position === right) && leftExceed && rightExceed ? top : position; // first, when both left and right limits are exceeded, we fall back to top|bottom
  267. position = position === top && topExceed ? bottom : position;
  268. position = position === bottom && bottomExceed ? top : position;
  269. position = position === left && leftExceed ? right : position;
  270. position = position === right && rightExceed ? left : position;
  271. // apply styling to tooltip or popover
  272. if ( position === left || position === right ) { // secondary|side positions
  273. if ( position === left ) { // LEFT
  274. leftPosition = rect[left] + scroll.x - elementDimensions.w;
  275. } else { // RIGHT
  276. leftPosition = rect[left] + scroll.x + linkDimensions.w;
  277. }
  278. // adjust top and arrow
  279. if (halfTopExceed) {
  280. topPosition = rect[top] + scroll.y;
  281. arrowTop = linkDimensions.h/2;
  282. } else if (halfBottomExceed) {
  283. topPosition = rect[top] + scroll.y - elementDimensions.h + linkDimensions.h;
  284. arrowTop = elementDimensions.h - linkDimensions.h/2;
  285. } else {
  286. topPosition = rect[top] + scroll.y - elementDimensions.h/2 + linkDimensions.h/2;
  287. }
  288. } else if ( position === top || position === bottom ) { // primary|vertical positions
  289. if ( position === top) { // TOP
  290. topPosition = rect[top] + scroll.y - elementDimensions.h;
  291. } else { // BOTTOM
  292. topPosition = rect[top] + scroll.y + linkDimensions.h;
  293. }
  294. // adjust left | right and also the arrow
  295. if (halfLeftExceed) {
  296. leftPosition = 0;
  297. arrowLeft = rect[left] + linkDimensions.w/2;
  298. } else if (halfRightExceed) {
  299. leftPosition = windowWidth - elementDimensions.w*1.01;
  300. arrowLeft = elementDimensions.w - ( windowWidth - rect[left] ) + linkDimensions.w/2;
  301. } else {
  302. leftPosition = rect[left] + scroll.x - elementDimensions.w/2 + linkDimensions.w/2;
  303. }
  304. }
  305. // apply style to tooltip/popover and it's arrow
  306. element[style][top] = topPosition + 'px';
  307. element[style][left] = leftPosition + 'px';
  308. arrowTop && (arrow[style][top] = arrowTop + 'px');
  309. arrowLeft && (arrow[style][left] = arrowLeft + 'px');
  310. element.className[indexOf](position) === -1 && (element.className = element.className.replace(tipPositions,position));
  311. };
  312. BSN.version = '2.0.27';
  313. /* Native Javascript for Bootstrap 3 | Affix
  314. -------------------------------------------*/
  315. // AFFIX DEFINITION
  316. var Affix = function(element, options) {
  317. // initialization element
  318. element = queryElement(element);
  319. // set options
  320. options = options || {};
  321. // read DATA API
  322. var targetData = element[getAttribute](dataTarget),
  323. offsetTopData = element[getAttribute](dataOffsetTop),
  324. offsetBottomData = element[getAttribute](dataOffsetBottom),
  325. // component specific strings
  326. affix = 'affix', affixed = 'affixed', fn = 'function', update = 'update',
  327. affixTop = 'affix-top', affixedTop = 'affixed-top',
  328. affixBottom = 'affix-bottom', affixedBottom = 'affixed-bottom';
  329. this[target] = options[target] ? queryElement(options[target]) : queryElement(targetData) || null; // target is an object
  330. this[offsetTop] = options[offsetTop] ? options[offsetTop] : parseInt(offsetTopData) || 0; // offset option is an integer number or function to determine that number
  331. this[offsetBottom] = options[offsetBottom] ? options[offsetBottom]: parseInt(offsetBottomData) || 0;
  332. if ( !this[target] && !( this[offsetTop] || this[offsetBottom] ) ) { return; } // invalidate
  333. // internal bind
  334. var self = this,
  335. // constants
  336. pinOffsetTop, pinOffsetBottom, maxScroll, scrollY, pinnedTop, pinnedBottom,
  337. affixedToTop = false, affixedToBottom = false,
  338. // private methods
  339. getMaxScroll = function(){
  340. return Math.max( DOC[body][scrollHeight], DOC[body][offsetHeight], HTML[clientHeight], HTML[scrollHeight], HTML[offsetHeight] );
  341. },
  342. getOffsetTop = function () {
  343. if ( self[target] !== null ) {
  344. return self[target][getBoundingClientRect]()[top] + scrollY;
  345. } else if ( self[offsetTop] ) {
  346. return parseInt(typeof self[offsetTop] === fn ? self[offsetTop]() : self[offsetTop] || 0);
  347. }
  348. },
  349. getOffsetBottom = function () {
  350. if ( self[offsetBottom] ) {
  351. return maxScroll - element[offsetHeight] - parseInt( typeof self[offsetBottom] === fn ? self[offsetBottom]() : self[offsetBottom] || 0 );
  352. }
  353. },
  354. checkPosition = function () {
  355. maxScroll = getMaxScroll();
  356. scrollY = parseInt(getScroll().y,0);
  357. pinOffsetTop = getOffsetTop();
  358. pinOffsetBottom = getOffsetBottom();
  359. pinnedTop = ( parseInt(pinOffsetTop) - scrollY < 0) && (scrollY > parseInt(pinOffsetTop) );
  360. pinnedBottom = ( parseInt(pinOffsetBottom) - scrollY < 0) && (scrollY > parseInt(pinOffsetBottom) );
  361. },
  362. pinTop = function () {
  363. if ( !affixedToTop && !hasClass(element,affix) ) { // on loading a page halfway scrolled these events don't trigger in Chrome
  364. bootstrapCustomEvent.call(element, affix, affix);
  365. bootstrapCustomEvent.call(element, affixTop, affix);
  366. addClass(element,affix);
  367. affixedToTop = true;
  368. bootstrapCustomEvent.call(element, affixed, affix);
  369. bootstrapCustomEvent.call(element, affixedTop, affix);
  370. }
  371. },
  372. unPinTop = function () {
  373. if ( affixedToTop && hasClass(element,affix) ) {
  374. removeClass(element,affix);
  375. affixedToTop = false;
  376. }
  377. },
  378. pinBottom = function () {
  379. if ( !affixedToBottom && !hasClass(element, affixBottom) ) {
  380. bootstrapCustomEvent.call(element, affix, affix);
  381. bootstrapCustomEvent.call(element, affixBottom, affix);
  382. addClass(element,affixBottom);
  383. affixedToBottom = true;
  384. bootstrapCustomEvent.call(element, affixed, affix);
  385. bootstrapCustomEvent.call(element, affixedBottom, affix);
  386. }
  387. },
  388. unPinBottom = function () {
  389. if ( affixedToBottom && hasClass(element,affixBottom) ) {
  390. removeClass(element,affixBottom);
  391. affixedToBottom = false;
  392. }
  393. },
  394. updatePin = function () {
  395. if ( pinnedBottom ) {
  396. if ( pinnedTop ) { unPinTop(); }
  397. pinBottom();
  398. } else {
  399. unPinBottom();
  400. if ( pinnedTop ) { pinTop(); }
  401. else { unPinTop(); }
  402. }
  403. };
  404. // public method
  405. this[update] = function () {
  406. checkPosition();
  407. updatePin();
  408. };
  409. // init
  410. if ( !(stringAffix in element ) ) { // prevent adding event handlers twice
  411. on( globalObject, scrollEvent, self[update], passiveHandler );
  412. !isIE8 && on( globalObject, resizeEvent, self[update], passiveHandler );
  413. }
  414. element[stringAffix] = self;
  415. self[update]();
  416. };
  417. // AFFIX DATA API
  418. // =================
  419. supports[push]([stringAffix, Affix, '['+dataSpy+'="affix"]']);
  420. /* Native Javascript for Bootstrap 3 | Alert
  421. -------------------------------------------*/
  422. // ALERT DEFINITION
  423. // ================
  424. var Alert = function( element ) {
  425. // initialization element
  426. element = queryElement(element);
  427. // bind, target alert, duration and stuff
  428. var self = this, component = 'alert',
  429. alert = getClosest(element,'.'+component),
  430. triggerHandler = function(){ hasClass(alert,'fade') ? emulateTransitionEnd(alert,transitionEndHandler) : transitionEndHandler(); },
  431. // handlers
  432. clickHandler = function(e){
  433. alert = getClosest(e[target],'.'+component);
  434. element = queryElement('['+dataDismiss+'="'+component+'"]',alert);
  435. element && alert && (element === e[target] || element[contains](e[target])) && self.close();
  436. },
  437. transitionEndHandler = function(){
  438. bootstrapCustomEvent.call(alert, closedEvent, component);
  439. off(element, clickEvent, clickHandler); // detach it's listener
  440. alert[parentNode].removeChild(alert);
  441. };
  442. // public method
  443. this.close = function() {
  444. if ( alert && element && hasClass(alert,inClass) ) {
  445. bootstrapCustomEvent.call(alert, closeEvent, component);
  446. removeClass(alert,inClass);
  447. alert && triggerHandler();
  448. }
  449. };
  450. // init
  451. if ( !(stringAlert in element ) ) { // prevent adding event handlers twice
  452. on(element, clickEvent, clickHandler);
  453. }
  454. element[stringAlert] = self;
  455. };
  456. // ALERT DATA API
  457. // ==============
  458. supports[push]([stringAlert, Alert, '['+dataDismiss+'="alert"]']);
  459. /* Native Javascript for Bootstrap 3 | Button
  460. ---------------------------------------------*/
  461. // BUTTON DEFINITION
  462. // ===================
  463. var Button = function( element, option ) {
  464. // initialization element
  465. element = queryElement(element);
  466. // set option
  467. option = option || null;
  468. // constant
  469. var toggled = false, // toggled makes sure to prevent triggering twice the change.bs.button events
  470. // strings
  471. component = 'button',
  472. checked = 'checked',
  473. reset = 'reset',
  474. LABEL = 'LABEL',
  475. INPUT = 'INPUT',
  476. // private methods
  477. setState = function() {
  478. if ( !! option && option !== reset ) {
  479. if ( option === loading ) {
  480. addClass(element,disabled);
  481. element[setAttribute](disabled,disabled);
  482. element[setAttribute](dataOriginalText, element[innerHTML].trim()); // trim the text
  483. }
  484. element[innerHTML] = element[getAttribute]('data-'+option+'-text');
  485. }
  486. },
  487. resetState = function() {
  488. if (element[getAttribute](dataOriginalText)) {
  489. if ( hasClass(element,disabled) || element[getAttribute](disabled) === disabled ) {
  490. removeClass(element,disabled);
  491. element.removeAttribute(disabled);
  492. }
  493. element[innerHTML] = element[getAttribute](dataOriginalText);
  494. }
  495. },
  496. keyHandler = function(e){
  497. var key = e.which || e.keyCode;
  498. key === 32 && e[target] === DOC.activeElement && toggle(e);
  499. },
  500. preventScroll = function(e){
  501. var key = e.which || e.keyCode;
  502. key === 32 && e[preventDefault]();
  503. },
  504. toggle = function(e) {
  505. var label = e[target].tagName === LABEL ? e[target] : e[target][parentNode].tagName === LABEL ? e[target][parentNode] : null; // the .btn label
  506. if ( !label ) return; //react if a label or its immediate child is clicked
  507. var labels = getElementsByClassName(label[parentNode],'btn'), // all the button group buttons
  508. input = label[getElementsByTagName](INPUT)[0];
  509. if ( !input ) return; // return if no input found
  510. // manage the dom manipulation
  511. if ( input.type === 'checkbox' ) { //checkboxes
  512. if ( !input[checked] ) {
  513. addClass(label,active);
  514. input[getAttribute](checked);
  515. input[setAttribute](checked,checked);
  516. input[checked] = true;
  517. } else {
  518. removeClass(label,active);
  519. input[getAttribute](checked);
  520. input.removeAttribute(checked);
  521. input[checked] = false;
  522. }
  523. if (!toggled) { // prevent triggering the event twice
  524. toggled = true;
  525. bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input
  526. bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group
  527. }
  528. }
  529. if ( input.type === 'radio' && !toggled ) { // radio buttons
  530. // don't trigger if already active (the OR condition is a hack to check if the buttons were selected with key press and NOT mouse click)
  531. if ( !input[checked] || (e.screenX === 0 && e.screenY == 0) ) {
  532. addClass(label,active);
  533. input[setAttribute](checked,checked);
  534. input[checked] = true;
  535. bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input
  536. bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group
  537. toggled = true;
  538. for (var i = 0, ll = labels[length]; i<ll; i++) {
  539. var otherLabel = labels[i], otherInput = otherLabel[getElementsByTagName](INPUT)[0];
  540. if ( otherLabel !== label && hasClass(otherLabel,active) ) {
  541. removeClass(otherLabel,active);
  542. otherInput.removeAttribute(checked);
  543. otherInput[checked] = false;
  544. bootstrapCustomEvent.call(otherInput, changeEvent, component); // trigger the change
  545. }
  546. }
  547. }
  548. }
  549. setTimeout( function() { toggled = false; }, 50 );
  550. };
  551. // init
  552. if ( hasClass(element,'btn') ) { // when Button text is used we execute it as an instance method
  553. if ( option !== null ) {
  554. if ( option !== reset ) { setState(); }
  555. else { resetState(); }
  556. }
  557. } else { // if ( hasClass(element,'btn-group') ) // we allow the script to work outside btn-group component
  558. if ( !( stringButton in element ) ) { // prevent adding event handlers twice
  559. on( element, clickEvent, toggle );
  560. on( element, keyupEvent, keyHandler ), on( element, keydownEvent, preventScroll );
  561. }
  562. // activate items on load
  563. var labelsToACtivate = getElementsByClassName(element, 'btn'), lbll = labelsToACtivate[length];
  564. for (var i=0; i<lbll; i++) {
  565. !hasClass(labelsToACtivate[i],active) && queryElement('input',labelsToACtivate[i])[getAttribute](checked)
  566. && addClass(labelsToACtivate[i],active);
  567. }
  568. element[stringButton] = this;
  569. }
  570. };
  571. // BUTTON DATA API
  572. // =================
  573. supports[push]( [ stringButton, Button, '['+dataToggle+'="buttons"]' ] );
  574. /* Native Javascript for Bootstrap 3 | Carousel
  575. ----------------------------------------------*/
  576. // CAROUSEL DEFINITION
  577. // ===================
  578. var Carousel = function( element, options ) {
  579. // initialization element
  580. element = queryElement( element );
  581. // set options
  582. options = options || {};
  583. // DATA API
  584. var intervalAttribute = element[getAttribute](dataInterval),
  585. intervalOption = options[interval],
  586. intervalData = intervalAttribute === 'false' ? 0 : parseInt(intervalAttribute),
  587. pauseData = element[getAttribute](dataPause) === hoverEvent || false,
  588. keyboardData = element[getAttribute](dataKeyboard) === 'true' || false,
  589. // strings
  590. component = 'carousel',
  591. paused = 'paused',
  592. direction = 'direction',
  593. dataSlideTo = 'data-slide-to';
  594. this[keyboard] = options[keyboard] === true || keyboardData;
  595. this[pause] = (options[pause] === hoverEvent || pauseData) ? hoverEvent : false; // false / hover
  596. this[interval] = typeof intervalOption === 'number' ? intervalOption
  597. : intervalOption === false || intervalData === 0 || intervalData === false ? 0
  598. : isNaN(intervalData) ? 5000 // bootstrap carousel default interval
  599. : intervalData;
  600. // bind, event targets
  601. var self = this, index = element.index = 0, timer = element.timer = 0,
  602. isSliding = false, // isSliding prevents click event handlers when animation is running
  603. isTouch = false, startXPosition = null, currentXPosition = null, endXPosition = null, // touch and event coordinates
  604. slides = getElementsByClassName(element,'item'), total = slides[length],
  605. slideDirection = this[direction] = left,
  606. controls = getElementsByClassName(element,component+'-control'),
  607. leftArrow = controls[0], rightArrow = controls[1],
  608. indicator = queryElement( '.'+component+'-indicators', element ),
  609. indicators = indicator && indicator[getElementsByTagName]( "LI" ) || [];
  610. // invalidate when not enough items
  611. if (total < 2) { return; }
  612. // handlers
  613. var pauseHandler = function () {
  614. if ( self[interval] !==false && !hasClass(element,paused) ) {
  615. addClass(element,paused);
  616. !isSliding && ( clearInterval(timer), timer = null );
  617. }
  618. },
  619. resumeHandler = function() {
  620. if ( self[interval] !== false && hasClass(element,paused) ) {
  621. removeClass(element,paused);
  622. !isSliding && ( clearInterval(timer), timer = null );
  623. !isSliding && self.cycle();
  624. }
  625. },
  626. indicatorHandler = function(e) {
  627. e[preventDefault]();
  628. if (isSliding) return;
  629. var eventTarget = e[target]; // event target | the current active item
  630. if ( eventTarget && !hasClass(eventTarget,active) && eventTarget[getAttribute](dataSlideTo) ) {
  631. index = parseInt( eventTarget[getAttribute](dataSlideTo), 10 );
  632. } else { return false; }
  633. self.slideTo( index ); //Do the slide
  634. },
  635. controlsHandler = function (e) {
  636. e[preventDefault]();
  637. if (isSliding) return;
  638. var eventTarget = e.currentTarget || e.srcElement;
  639. if ( eventTarget === rightArrow ) {
  640. index++;
  641. } else if ( eventTarget === leftArrow ) {
  642. index--;
  643. }
  644. self.slideTo( index ); //Do the slide
  645. },
  646. keyHandler = function (e) {
  647. if (isSliding) return;
  648. switch (e.which) {
  649. case 39:
  650. index++;
  651. break;
  652. case 37:
  653. index--;
  654. break;
  655. default: return;
  656. }
  657. self.slideTo( index ); //Do the slide
  658. },
  659. // touch events
  660. toggleTouchEvents = function(toggle){
  661. toggle( element, touchEvents.move, touchMoveHandler, passiveHandler );
  662. toggle( element, touchEvents.end, touchEndHandler, passiveHandler );
  663. },
  664. touchDownHandler = function(e) {
  665. if ( isTouch ) { return; }
  666. startXPosition = parseInt(e.touches[0].pageX);
  667. if ( element.contains(e[target]) ) {
  668. isTouch = true;
  669. toggleTouchEvents(on);
  670. }
  671. },
  672. touchMoveHandler = function(e) {
  673. if ( !isTouch ) { e.preventDefault(); return; }
  674. currentXPosition = parseInt(e.touches[0].pageX);
  675. //cancel touch if more than one touches detected
  676. if ( e.type === 'touchmove' && e.touches[length] > 1 ) {
  677. e.preventDefault();
  678. return false;
  679. }
  680. },
  681. touchEndHandler = function(e) {
  682. if ( !isTouch || isSliding ) { return }
  683. endXPosition = currentXPosition || parseInt( e.touches[0].pageX );
  684. if ( isTouch ) {
  685. if ( (!element.contains(e[target]) || !element.contains(e.relatedTarget) ) && Math.abs(startXPosition - endXPosition) < 75 ) {
  686. return false;
  687. } else {
  688. if ( currentXPosition < startXPosition ) {
  689. index++;
  690. } else if ( currentXPosition > startXPosition ) {
  691. index--;
  692. }
  693. isTouch = false;
  694. self.slideTo(index);
  695. }
  696. toggleTouchEvents(off);
  697. }
  698. },
  699. // private methods
  700. isElementInScrollRange = function () {
  701. var rect = element[getBoundingClientRect](),
  702. viewportHeight = globalObject[innerHeight] || HTML[clientHeight]
  703. return rect[top] <= viewportHeight && rect[bottom] >= 0; // bottom && top
  704. },
  705. setActivePage = function( pageIndex ) { //indicators
  706. for ( var i = 0, icl = indicators[length]; i < icl; i++ ) {
  707. removeClass(indicators[i],active);
  708. }
  709. if (indicators[pageIndex]) addClass(indicators[pageIndex], active);
  710. };
  711. // public methods
  712. this.cycle = function() {
  713. if (timer) {
  714. clearInterval(timer);
  715. timer = null;
  716. }
  717. timer = setInterval(function() {
  718. isElementInScrollRange() && (index++, self.slideTo( index ) );
  719. }, this[interval]);
  720. };
  721. this.slideTo = function( next ) {
  722. if (isSliding) return; // when controled via methods, make sure to check again
  723. var activeItem = this.getActiveIndex(), // the current active
  724. orientation;
  725. // first return if we're on the same item #227
  726. if ( activeItem === next ) {
  727. return;
  728. // or determine slideDirection
  729. } else if ( (activeItem < next ) || (activeItem === 0 && next === total -1 ) ) {
  730. slideDirection = self[direction] = left; // next
  731. } else if ( (activeItem > next) || (activeItem === total - 1 && next === 0 ) ) {
  732. slideDirection = self[direction] = right; // prev
  733. }
  734. // find the right next index
  735. if ( next < 0 ) { next = total - 1; }
  736. else if ( next >= total ){ next = 0; }
  737. // update index
  738. index = next;
  739. orientation = slideDirection === left ? 'next' : 'prev'; //determine type
  740. bootstrapCustomEvent.call(element, slideEvent, component, slides[next]); // here we go with the slide
  741. isSliding = true;
  742. clearInterval(timer);
  743. timer = null;
  744. setActivePage( next );
  745. if ( supportTransitions && hasClass(element,'slide') ) {
  746. addClass(slides[next],orientation);
  747. slides[next][offsetWidth];
  748. addClass(slides[next],slideDirection);
  749. addClass(slides[activeItem],slideDirection);
  750. emulateTransitionEnd(slides[next], function(e) {
  751. var timeout = e && e[target] !== slides[next] ? e.elapsedTime*1000+100 : 20;
  752. isSliding && setTimeout(function(){
  753. isSliding = false;
  754. addClass(slides[next],active);
  755. removeClass(slides[activeItem],active);
  756. removeClass(slides[next],orientation);
  757. removeClass(slides[next],slideDirection);
  758. removeClass(slides[activeItem],slideDirection);
  759. bootstrapCustomEvent.call(element, slidEvent, component, slides[next]);
  760. if ( self[interval] && !hasClass(element,paused) ) {
  761. self.cycle();
  762. }
  763. }, timeout);
  764. });
  765. } else {
  766. addClass(slides[next],active);
  767. slides[next][offsetWidth];
  768. removeClass(slides[activeItem],active);
  769. setTimeout(function() {
  770. isSliding = false;
  771. if ( self[interval] && !hasClass(element,paused) ) {
  772. self.cycle();
  773. }
  774. bootstrapCustomEvent.call(element, slidEvent, component, slides[next]);
  775. }, 100 );
  776. }
  777. };
  778. this.getActiveIndex = function () {
  779. return slides[indexOf](getElementsByClassName(element,'item active')[0]) || 0;
  780. };
  781. // init
  782. if ( !(stringCarousel in element ) ) { // prevent adding event handlers twice
  783. if ( self[pause] && self[interval] ) {
  784. on( element, mouseHover[0], pauseHandler );
  785. on( element, mouseHover[1], resumeHandler );
  786. on( element, touchEvents.start, pauseHandler, passiveHandler );
  787. on( element, touchEvents.end, resumeHandler, passiveHandler );
  788. }
  789. slides[length] > 1 && on( element, touchEvents.start, touchDownHandler, passiveHandler );
  790. rightArrow && on( rightArrow, clickEvent, controlsHandler );
  791. leftArrow && on( leftArrow, clickEvent, controlsHandler );
  792. indicator && on( indicator, clickEvent, indicatorHandler );
  793. self[keyboard] && on( globalObject, keydownEvent, keyHandler );
  794. }
  795. if (self.getActiveIndex()<0) {
  796. slides[length] && addClass(slides[0],active);
  797. indicators[length] && setActivePage(0);
  798. }
  799. if ( self[interval] ){ self.cycle(); }
  800. element[stringCarousel] = self;
  801. };
  802. // CAROUSEL DATA API
  803. // =================
  804. supports[push]( [ stringCarousel, Carousel, '['+dataRide+'="carousel"]' ] );
  805. /* Native Javascript for Bootstrap 3 | Collapse
  806. -----------------------------------------------*/
  807. // COLLAPSE DEFINITION
  808. // ===================
  809. var Collapse = function( element, options ) {
  810. // initialization element
  811. element = queryElement(element);
  812. // set options
  813. options = options || {};
  814. // event targets and constants
  815. var accordion = null, collapse = null, self = this,
  816. accordionData = element[getAttribute]('data-parent'),
  817. activeCollapse, activeElement,
  818. // component strings
  819. component = 'collapse',
  820. collapsed = 'collapsed',
  821. isAnimating = 'isAnimating',
  822. // private methods
  823. openAction = function(collapseElement,toggle) {
  824. bootstrapCustomEvent.call(collapseElement, showEvent, component);
  825. collapseElement[isAnimating] = true;
  826. addClass(collapseElement,collapsing);
  827. removeClass(collapseElement,component);
  828. collapseElement[style][height] = collapseElement[scrollHeight] + 'px';
  829. emulateTransitionEnd(collapseElement, function() {
  830. collapseElement[isAnimating] = false;
  831. collapseElement[setAttribute](ariaExpanded,'true');
  832. toggle[setAttribute](ariaExpanded,'true');
  833. removeClass(collapseElement,collapsing);
  834. addClass(collapseElement, component);
  835. addClass(collapseElement, inClass);
  836. collapseElement[style][height] = '';
  837. bootstrapCustomEvent.call(collapseElement, shownEvent, component);
  838. });
  839. },
  840. closeAction = function(collapseElement,toggle) {
  841. bootstrapCustomEvent.call(collapseElement, hideEvent, component);
  842. collapseElement[isAnimating] = true;
  843. collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; // set height first
  844. removeClass(collapseElement,component);
  845. removeClass(collapseElement, inClass);
  846. addClass(collapseElement, collapsing);
  847. collapseElement[offsetWidth]; // force reflow to enable transition
  848. collapseElement[style][height] = '0px';
  849. emulateTransitionEnd(collapseElement, function() {
  850. collapseElement[isAnimating] = false;
  851. collapseElement[setAttribute](ariaExpanded,'false');
  852. toggle[setAttribute](ariaExpanded,'false');
  853. removeClass(collapseElement,collapsing);
  854. addClass(collapseElement,component);
  855. collapseElement[style][height] = '';
  856. bootstrapCustomEvent.call(collapseElement, hiddenEvent, component);
  857. });
  858. },
  859. getTarget = function() {
  860. var href = element.href && element[getAttribute]('href'),
  861. parent = element[getAttribute](dataTarget),
  862. id = href || ( parent && parent.charAt(0) === '#' ) && parent;
  863. return id && queryElement(id);
  864. };
  865. // public methods
  866. this.toggle = function(e) {
  867. e[preventDefault]();
  868. if (!hasClass(collapse,inClass)) { self.show(); }
  869. else { self.hide(); }
  870. };
  871. this.hide = function() {
  872. if ( collapse[isAnimating] ) return;
  873. closeAction(collapse,element);
  874. addClass(element,collapsed);
  875. };
  876. this.show = function() {
  877. if ( accordion ) {
  878. activeCollapse = queryElement('.'+component+'.'+inClass,accordion);
  879. activeElement = activeCollapse && (queryElement('['+dataTarget+'="#'+activeCollapse.id+'"]', accordion)
  880. || queryElement('[href="#'+activeCollapse.id+'"]',accordion) );
  881. }
  882. if ( !collapse[isAnimating] || activeCollapse && !activeCollapse[isAnimating] ) {
  883. if ( activeElement && activeCollapse !== collapse ) {
  884. closeAction(activeCollapse,activeElement);
  885. addClass(activeElement,collapsed);
  886. }
  887. openAction(collapse,element);
  888. removeClass(element,collapsed);
  889. }
  890. };
  891. // init
  892. if ( !(stringCollapse in element ) ) { // prevent adding event handlers twice
  893. on(element, clickEvent, self.toggle);
  894. }
  895. collapse = getTarget();
  896. collapse[isAnimating] = false; // when true it will prevent click handlers
  897. accordion = queryElement(options.parent) || accordionData && getClosest(element, accordionData);
  898. element[stringCollapse] = self;
  899. };
  900. // COLLAPSE DATA API
  901. // =================
  902. supports[push]( [ stringCollapse, Collapse, '['+dataToggle+'="collapse"]' ] );
  903. /* Native Javascript for Bootstrap 3 | Dropdown
  904. ----------------------------------------------*/
  905. // DROPDOWN DEFINITION
  906. // ===================
  907. var Dropdown = function( element, option ) {
  908. // initialization element
  909. element = queryElement(element);
  910. // set option
  911. this.persist = option === true || element[getAttribute]('data-persist') === 'true' || false;
  912. // constants, event targets, strings
  913. var self = this, children = 'children',
  914. parent = element[parentNode],
  915. component = 'dropdown', open = 'open',
  916. relatedTarget = null,
  917. menu = queryElement('.dropdown-menu', parent),
  918. menuItems = (function(){
  919. var set = menu[children], newSet = [];
  920. for ( var i=0; i<set[length]; i++ ){
  921. set[i][children][length] && (set[i][children][0].tagName === 'A' && newSet[push](set[i]));
  922. }
  923. return newSet;
  924. })(),
  925. // preventDefault on empty anchor links
  926. preventEmptyAnchor = function(anchor){
  927. (anchor.href && anchor.href.slice(-1) === '#' || anchor[parentNode] && anchor[parentNode].href
  928. && anchor[parentNode].href.slice(-1) === '#') && this[preventDefault]();
  929. },
  930. // toggle dismissible events
  931. toggleDismiss = function(){
  932. var type = element[open] ? on : off;
  933. type(DOC, clickEvent, dismissHandler);
  934. type(DOC, keydownEvent, preventScroll);
  935. type(DOC, keyupEvent, keyHandler);
  936. type(DOC, focusEvent, dismissHandler, true);
  937. },
  938. // handlers
  939. dismissHandler = function(e) {
  940. var eventTarget = e[target], hasData = eventTarget && (eventTarget[getAttribute](dataToggle)
  941. || eventTarget[parentNode] && getAttribute in eventTarget[parentNode]
  942. && eventTarget[parentNode][getAttribute](dataToggle));
  943. if ( e.type === focusEvent && (eventTarget === element || eventTarget === menu || menu[contains](eventTarget) ) ) {
  944. return;
  945. }
  946. if ( (eventTarget === menu || menu[contains](eventTarget)) && (self.persist || hasData) ) { return; }
  947. else {
  948. relatedTarget = eventTarget === element || element[contains](eventTarget) ? element : null;
  949. hide();
  950. }
  951. preventEmptyAnchor.call(e,eventTarget);
  952. },
  953. clickHandler = function(e) {
  954. relatedTarget = element;
  955. show();
  956. preventEmptyAnchor.call(e,e[target]);
  957. },
  958. preventScroll = function(e){
  959. var key = e.which || e.keyCode;
  960. if( key === 38 || key === 40 ) { e[preventDefault](); }
  961. },
  962. keyHandler = function(e){
  963. var key = e.which || e.keyCode,
  964. activeItem = DOC.activeElement,
  965. idx = menuItems[indexOf](activeItem[parentNode]),
  966. isSameElement = activeItem === element,
  967. isInsideMenu = menu[contains](activeItem),
  968. isMenuItem = activeItem[parentNode][parentNode] === menu;
  969. if ( isMenuItem ) { // navigate up | down
  970. idx = isSameElement ? 0
  971. : key === 38 ? (idx>1?idx-1:0)
  972. : key === 40 ? (idx<menuItems[length]-1?idx+1:idx) : idx;
  973. menuItems[idx] && setFocus(menuItems[idx][children][0]);
  974. }
  975. if ( (menuItems[length] && isMenuItem // menu has items
  976. || !menuItems[length] && (isInsideMenu || isSameElement) // menu might be a form
  977. || !isInsideMenu ) // or the focused element is not in the menu at all
  978. && element[open] && key === 27 // menu must be open
  979. ) {
  980. self.toggle();
  981. relatedTarget = null;
  982. }
  983. },
  984. // private methods
  985. show = function() {
  986. bootstrapCustomEvent.call(parent, showEvent, component, relatedTarget);
  987. addClass(parent,open);
  988. element[setAttribute](ariaExpanded,true);
  989. bootstrapCustomEvent.call(parent, shownEvent, component, relatedTarget);
  990. element[open] = true;
  991. off(element, clickEvent, clickHandler);
  992. setTimeout(function(){
  993. setFocus( menu[getElementsByTagName]('INPUT')[0] || element ); // focus the first input item | element
  994. toggleDismiss();
  995. },1);
  996. },
  997. hide = function() {
  998. bootstrapCustomEvent.call(parent, hideEvent, component, relatedTarget);
  999. removeClass(parent,open);
  1000. element[setAttribute](ariaExpanded,false);
  1001. bootstrapCustomEvent.call(parent, hiddenEvent, component, relatedTarget);
  1002. element[open] = false;
  1003. toggleDismiss();
  1004. setFocus(element);
  1005. setTimeout(function(){ on(element, clickEvent, clickHandler); },1);
  1006. };
  1007. // set initial state to closed
  1008. element[open] = false;
  1009. // public methods
  1010. this.toggle = function() {
  1011. if (hasClass(parent,open) && element[open]) { hide(); }
  1012. else { show(); }
  1013. };
  1014. // init
  1015. if (!(stringDropdown in element)) { // prevent adding event handlers twice
  1016. !tabindex in menu && menu[setAttribute](tabindex, '0'); // Fix onblur on Chrome | Safari
  1017. on(element, clickEvent, clickHandler);
  1018. }
  1019. element[stringDropdown] = self;
  1020. };
  1021. // DROPDOWN DATA API
  1022. // =================
  1023. supports[push]( [stringDropdown, Dropdown, '['+dataToggle+'="dropdown"]'] );
  1024. /* Native Javascript for Bootstrap 3 | Modal
  1025. -------------------------------------------*/
  1026. // MODAL DEFINITION
  1027. // ===============
  1028. var Modal = function(element, options) { // element can be the modal/triggering button
  1029. // the modal (both JavaScript / DATA API init) / triggering button element (DATA API)
  1030. element = queryElement(element);
  1031. // strings
  1032. var component = 'modal',
  1033. staticString = 'static',
  1034. modalTrigger = 'modalTrigger',
  1035. paddingRight = 'paddingRight',
  1036. modalBackdropString = 'modal-backdrop',
  1037. isAnimating = 'isAnimating',
  1038. // determine modal, triggering element
  1039. btnCheck = element[getAttribute](dataTarget)||element[getAttribute]('href'),
  1040. checkModal = queryElement( btnCheck ),
  1041. modal = hasClass(element,component) ? element : checkModal;
  1042. if ( hasClass(element, component) ) { element = null; } // modal is now independent of it's triggering element
  1043. if ( !modal ) { return; } // invalidate
  1044. // set options
  1045. options = options || {};
  1046. this[keyboard] = options[keyboard] === false || modal[getAttribute](dataKeyboard) === 'false' ? false : true;
  1047. this[backdrop] = options[backdrop] === staticString || modal[getAttribute](databackdrop) === staticString ? staticString : true;
  1048. this[backdrop] = options[backdrop] === false || modal[getAttribute](databackdrop) === 'false' ? false : this[backdrop];
  1049. this[animation] = hasClass(modal, 'fade') ? true : false;
  1050. this[content] = options[content]; // JavaScript only
  1051. // set an initial state of the modal
  1052. modal[isAnimating] = false;
  1053. // bind, constants, event targets and other vars
  1054. var self = this, relatedTarget = null,
  1055. bodyIsOverflowing, scrollBarWidth, overlay, overlayDelay, modalTimer,
  1056. // also find fixed-top / fixed-bottom items
  1057. fixedItems = getElementsByClassName(HTML,fixedTop).concat(getElementsByClassName(HTML,fixedBottom)),
  1058. // private methods
  1059. getWindowWidth = function() {
  1060. var htmlRect = HTML[getBoundingClientRect]();
  1061. return globalObject[innerWidth] || (htmlRect[right] - Math.abs(htmlRect[left]));
  1062. },
  1063. setScrollbar = function () {
  1064. var bodyStyle = DOC[body].currentStyle || globalObject[getComputedStyle](DOC[body]),
  1065. bodyPad = parseInt((bodyStyle[paddingRight]), 10), itemPad;
  1066. if (bodyIsOverflowing) {
  1067. DOC[body][style][paddingRight] = (bodyPad + scrollBarWidth) + 'px';
  1068. modal[style][paddingRight] = scrollBarWidth+'px';
  1069. if (fixedItems[length]){
  1070. for (var i = 0; i < fixedItems[length]; i++) {
  1071. itemPad = (fixedItems[i].currentStyle || globalObject[getComputedStyle](fixedItems[i]))[paddingRight];
  1072. fixedItems[i][style][paddingRight] = ( parseInt(itemPad) + scrollBarWidth) + 'px';
  1073. }
  1074. }
  1075. }
  1076. },
  1077. resetScrollbar = function () {
  1078. DOC[body][style][paddingRight] = '';
  1079. modal[style][paddingRight] = '';
  1080. if (fixedItems[length]){
  1081. for (var i = 0; i < fixedItems[length]; i++) {
  1082. fixedItems[i][style][paddingRight] = '';
  1083. }
  1084. }
  1085. },
  1086. measureScrollbar = function () { // thx walsh
  1087. var scrollDiv = DOC[createElement]('div'), widthValue;
  1088. scrollDiv.className = component+'-scrollbar-measure'; // this is here to stay
  1089. DOC[body][appendChild](scrollDiv);
  1090. widthValue = scrollDiv[offsetWidth] - scrollDiv[clientWidth];
  1091. DOC[body].removeChild(scrollDiv);
  1092. return widthValue;
  1093. },
  1094. checkScrollbar = function () {
  1095. bodyIsOverflowing = DOC[body][clientWidth] < getWindowWidth();
  1096. scrollBarWidth = measureScrollbar();
  1097. },
  1098. createOverlay = function() {
  1099. var newOverlay = DOC[createElement]('div');
  1100. overlay = queryElement('.'+modalBackdropString);
  1101. if ( overlay === null ) {
  1102. newOverlay[setAttribute]('class', modalBackdropString + (self[animation] ? ' fade' : ''));
  1103. overlay = newOverlay;
  1104. DOC[body][appendChild](overlay);
  1105. }
  1106. modalOverlay = 1;
  1107. },
  1108. removeOverlay = function() {
  1109. overlay = queryElement('.'+modalBackdropString);
  1110. if ( overlay && overlay !== null && typeof overlay === 'object' ) {
  1111. modalOverlay = 0;
  1112. DOC[body].removeChild(overlay); overlay = null;
  1113. }
  1114. },
  1115. // triggers
  1116. triggerShow = function() {
  1117. setFocus(modal);
  1118. modal[isAnimating] = false;
  1119. bootstrapCustomEvent.call(modal, shownEvent, component, relatedTarget);
  1120. on(globalObject, resizeEvent, self.update, passiveHandler);
  1121. on(modal, clickEvent, dismissHandler);
  1122. on(DOC, keydownEvent, keyHandler);
  1123. },
  1124. triggerHide = function() {
  1125. modal[style].display = '';
  1126. element && (setFocus(element));
  1127. bootstrapCustomEvent.call(modal, hiddenEvent, component);
  1128. (function(){
  1129. if (!getElementsByClassName(DOC,component+' '+inClass)[0]) {
  1130. resetScrollbar();
  1131. removeClass(DOC[body],component+'-open');
  1132. overlay && hasClass(overlay,'fade') ? (removeClass(overlay,inClass), emulateTransitionEnd(overlay,removeOverlay))
  1133. : removeOverlay();
  1134. off(globalObject, resizeEvent, self.update, passiveHandler);
  1135. off(modal, clickEvent, dismissHandler);
  1136. off(DOC, keydownEvent, keyHandler);
  1137. }
  1138. }());
  1139. modal[isAnimating] = false;
  1140. },
  1141. // handlers
  1142. clickHandler = function(e) {
  1143. if ( modal[isAnimating] ) return;
  1144. var clickTarget = e[target];
  1145. clickTarget = clickTarget[hasAttribute](dataTarget) || clickTarget[hasAttribute]('href') ? clickTarget : clickTarget[parentNode];
  1146. if ( clickTarget === element && !hasClass(modal,inClass) ) {
  1147. modal[modalTrigger] = element;
  1148. relatedTarget = element;
  1149. self.show();
  1150. e[preventDefault]();
  1151. }
  1152. },
  1153. keyHandler = function(e) {
  1154. if ( modal[isAnimating] ) return;
  1155. var key = e.which || e.keyCode; // keyCode for IE8
  1156. if (self[keyboard] && key == 27 && hasClass(modal,inClass)) {
  1157. self.hide();
  1158. }
  1159. },
  1160. dismissHandler = function(e) {
  1161. if ( modal[isAnimating] ) return;
  1162. var clickTarget = e[target];
  1163. if ( hasClass(modal,inClass) && (clickTarget[parentNode][getAttribute](dataDismiss) === component
  1164. || clickTarget[getAttribute](dataDismiss) === component
  1165. || clickTarget === modal && self[backdrop] !== staticString) ) {
  1166. self.hide(); relatedTarget = null;
  1167. e[preventDefault]();
  1168. }
  1169. };
  1170. // public methods
  1171. this.toggle = function() {
  1172. if ( hasClass(modal,inClass) ) {this.hide();} else {this.show();}
  1173. };
  1174. this.show = function() {
  1175. if ( hasClass(modal,inClass) || modal[isAnimating] ) {return}
  1176. clearTimeout(modalTimer);
  1177. modalTimer = setTimeout(function(){
  1178. modal[isAnimating] = true;
  1179. bootstrapCustomEvent.call(modal, showEvent, component, relatedTarget);
  1180. // we elegantly hide any opened modal
  1181. var currentOpen = getElementsByClassName(DOC,component+' in')[0];
  1182. if (currentOpen && currentOpen !== modal) {
  1183. modalTrigger in currentOpen && currentOpen[modalTrigger][stringModal].hide();
  1184. stringModal in currentOpen && currentOpen[stringModal].hide();
  1185. }
  1186. if ( self[backdrop] ) {
  1187. !modalOverlay && !overlay && createOverlay();
  1188. }
  1189. if ( overlay && !hasClass(overlay,inClass)) {
  1190. overlay[offsetWidth]; // force reflow to enable trasition
  1191. overlayDelay = getTransitionDurationFromElement(overlay);
  1192. addClass(overlay,inClass);
  1193. }
  1194. setTimeout( function() {
  1195. modal[style].display = 'block';
  1196. checkScrollbar();
  1197. setScrollbar();
  1198. addClass(DOC[body],component+'-open');
  1199. addClass(modal,inClass);
  1200. modal[setAttribute](ariaHidden, false);
  1201. hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerShow) : triggerShow();
  1202. }, supportTransitions && overlay && overlayDelay ? overlayDelay : 1);
  1203. },1);
  1204. };
  1205. this.hide = function() {
  1206. if ( modal[isAnimating] || !hasClass(modal,inClass) ) {return}
  1207. clearTimeout(modalTimer);
  1208. modalTimer = setTimeout(function(){
  1209. modal[isAnimating] = true;
  1210. bootstrapCustomEvent.call(modal, hideEvent, component);
  1211. overlay = queryElement('.'+modalBackdropString);
  1212. overlayDelay = overlay && getTransitionDurationFromElement(overlay);
  1213. removeClass(modal,inClass);
  1214. modal[setAttribute](ariaHidden, true);
  1215. setTimeout(function(){
  1216. hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerHide) : triggerHide();
  1217. }, supportTransitions && overlay && overlayDelay ? overlayDelay : 2);
  1218. },2)
  1219. };
  1220. this.setContent = function( content ) {
  1221. queryElement('.'+component+'-content',modal)[innerHTML] = content;
  1222. };
  1223. this.update = function() {
  1224. if (hasClass(modal,inClass)) {
  1225. checkScrollbar();
  1226. setScrollbar();
  1227. }
  1228. };
  1229. // init
  1230. // prevent adding event handlers over and over
  1231. // modal is independent of a triggering element
  1232. if ( !!element && !(stringModal in element) ) {
  1233. on(element, clickEvent, clickHandler);
  1234. }
  1235. if ( !!self[content] ) { self.setContent( self[content] ); }
  1236. if (element) { element[stringModal] = self; modal[modalTrigger] = element; }
  1237. else { modal[stringModal] = self; }
  1238. };
  1239. // DATA API
  1240. supports[push]( [ stringModal, Modal, '['+dataToggle+'="modal"]' ] );
  1241. /* Native Javascript for Bootstrap 3 | Popover
  1242. ----------------------------------------------*/
  1243. // POPOVER DEFINITION
  1244. // ==================
  1245. var Popover = function( element, options ) {
  1246. // initialization element
  1247. element = queryElement(element);
  1248. // set options
  1249. options = options || {};
  1250. // DATA API
  1251. var triggerData = element[getAttribute](dataTrigger), // click / hover / focus
  1252. animationData = element[getAttribute](dataAnimation), // true / false
  1253. placementData = element[getAttribute](dataPlacement),
  1254. dismissibleData = element[getAttribute](dataDismissible),
  1255. delayData = element[getAttribute](dataDelay),
  1256. containerData = element[getAttribute](dataContainer),
  1257. // internal strings
  1258. component = 'popover',
  1259. template = 'template',
  1260. trigger = 'trigger',
  1261. classString = 'class',
  1262. div = 'div',
  1263. fade = 'fade',
  1264. dataContent = 'data-content',
  1265. dismissible = 'dismissible',
  1266. closeBtn = '<button type="button" class="close">×</button>',
  1267. // check container
  1268. containerElement = queryElement(options[container]),
  1269. containerDataElement = queryElement(containerData),
  1270. // maybe the element is inside a modal
  1271. modal = getClosest(element,'.modal'),
  1272. // maybe the element is inside a fixed navbar
  1273. navbarFixedTop = getClosest(element,'.'+fixedTop),
  1274. navbarFixedBottom = getClosest(element,'.'+fixedBottom);
  1275. // set instance options
  1276. this[template] = options[template] ? options[template] : null; // JavaScript only
  1277. this[trigger] = options[trigger] ? options[trigger] : triggerData || hoverEvent;
  1278. this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade;
  1279. this[placement] = options[placement] ? options[placement] : placementData || top;
  1280. this[delay] = parseInt(options[delay] || delayData) || 200;
  1281. this[dismissible] = options[dismissible] || dismissibleData === 'true' ? true : false;
  1282. this[container] = containerElement ? containerElement
  1283. : containerDataElement ? containerDataElement
  1284. : navbarFixedTop ? navbarFixedTop
  1285. : navbarFixedBottom ? navbarFixedBottom
  1286. : modal ? modal : DOC[body];
  1287. // bind, content
  1288. var self = this,
  1289. titleString = options.title || element[getAttribute](dataTitle) || null,
  1290. contentString = options.content || element[getAttribute](dataContent) || null;
  1291. if ( !contentString && !this[template] ) return; // invalidate
  1292. // constants, vars
  1293. var popover = null, timer = 0, placementSetting = this[placement],
  1294. // handlers
  1295. dismissibleHandler = function(e) {
  1296. if (popover !== null && e[target] === queryElement('.close',popover)) {
  1297. self.hide();
  1298. }
  1299. },
  1300. // private methods
  1301. removePopover = function() {
  1302. self[container].removeChild(popover);
  1303. timer = null; popover = null;
  1304. },
  1305. createPopover = function() {
  1306. titleString = options.title || element[getAttribute](dataTitle);
  1307. contentString = options.content || element[getAttribute](dataContent);
  1308. // fixing https://github.com/thednp/bootstrap.native/issues/233
  1309. contentString = !!contentString ? contentString.replace(/^\s+|\s+$/g, '') : null;
  1310. popover = DOC[createElement](div);
  1311. if ( contentString !== null && self[template] === null ) { //create the popover from data attributes
  1312. popover[setAttribute]('role','tooltip');
  1313. if (titleString !== null) {
  1314. var popoverTitle = DOC[createElement]('h3');
  1315. popoverTitle[setAttribute](classString,component+'-title');
  1316. popoverTitle[innerHTML] = self[dismissible] ? titleString + closeBtn : titleString;
  1317. popover[appendChild](popoverTitle);
  1318. }
  1319. var popoverArrow = DOC[createElement](div), popoverContent = DOC[createElement](div);
  1320. popoverArrow[setAttribute](classString,'arrow'); popoverContent[setAttribute](classString,component+'-content');
  1321. popover[appendChild](popoverArrow); popover[appendChild](popoverContent);
  1322. //set popover content
  1323. popoverContent[innerHTML] = self[dismissible] && titleString === null ? contentString + closeBtn : contentString;
  1324. } else { // or create the popover from template
  1325. var popoverTemplate = DOC[createElement](div);
  1326. self[template] = self[template].replace(/^\s+|\s+$/g, '');
  1327. popoverTemplate[innerHTML] = self[template];
  1328. popover[innerHTML] = popoverTemplate.firstChild[innerHTML];
  1329. }
  1330. //append to the container
  1331. self[container][appendChild](popover);
  1332. popover[style].display = 'block';
  1333. popover[setAttribute](classString, component+ ' ' + placementSetting + ' ' + self[animation]);
  1334. },
  1335. showPopover = function () {
  1336. !hasClass(popover,inClass) && ( addClass(popover,inClass) );
  1337. },
  1338. updatePopover = function() {
  1339. styleTip(element,popover,placementSetting,self[container]);
  1340. },
  1341. // event toggle
  1342. dismissHandlerToggle = function(type){
  1343. if (clickEvent == self[trigger] || 'focus' == self[trigger]) {
  1344. !self[dismissible] && type( element, 'blur', self.hide );
  1345. }
  1346. self[dismissible] && type( DOC, clickEvent, dismissibleHandler );
  1347. !isIE8 && type( globalObject, resizeEvent, self.hide, passiveHandler );
  1348. },
  1349. // triggers
  1350. showTrigger = function() {
  1351. dismissHandlerToggle(on);
  1352. bootstrapCustomEvent.call(element, shownEvent, component);
  1353. },
  1354. hideTrigger = function() {
  1355. dismissHandlerToggle(off);
  1356. removePopover();
  1357. bootstrapCustomEvent.call(element, hiddenEvent, component);
  1358. };
  1359. // public methods / handlers
  1360. this.toggle = function() {
  1361. if (popover === null) { self.show(); }
  1362. else { self.hide(); }
  1363. };
  1364. this.show = function() {
  1365. clearTimeout(timer);
  1366. timer = setTimeout( function() {
  1367. if (popover === null) {
  1368. placementSetting = self[placement]; // we reset placement in all cases
  1369. createPopover();
  1370. updatePopover();
  1371. showPopover();
  1372. bootstrapCustomEvent.call(element, showEvent, component);
  1373. !!self[animation] ? emulateTransitionEnd(popover, showTrigger) : showTrigger();
  1374. }
  1375. }, 20 );
  1376. };
  1377. this.hide = function() {
  1378. clearTimeout(timer);
  1379. timer = setTimeout( function() {
  1380. if (popover && popover !== null && hasClass(popover,inClass)) {
  1381. bootstrapCustomEvent.call(element, hideEvent, component);
  1382. removeClass(popover,inClass);
  1383. !!self[animation] ? emulateTransitionEnd(popover, hideTrigger) : hideTrigger();
  1384. }
  1385. }, self[delay] );
  1386. };
  1387. // init
  1388. if ( !(stringPopover in element) ) { // prevent adding event handlers twice
  1389. if (self[trigger] === hoverEvent) {
  1390. on( element, mouseHover[0], self.show );
  1391. if (!self[dismissible]) { on( element, mouseHover[1], self.hide ); }
  1392. } else if (clickEvent == self[trigger] || 'focus' == self[trigger]) {
  1393. on( element, self[trigger], self.toggle );
  1394. }
  1395. }
  1396. element[stringPopover] = self;
  1397. };
  1398. // POPOVER DATA API
  1399. // ================
  1400. supports[push]( [ stringPopover, Popover, '['+dataToggle+'="popover"]' ] );
  1401. /* Native Javascript for Bootstrap 3 | ScrollSpy
  1402. -----------------------------------------------*/
  1403. // SCROLLSPY DEFINITION
  1404. // ====================
  1405. var ScrollSpy = function(element, options) {
  1406. // initialization element, the element we spy on
  1407. element = queryElement(element);
  1408. // DATA API
  1409. var targetData = queryElement(element[getAttribute](dataTarget)),
  1410. offsetData = element[getAttribute]('data-offset');
  1411. // set options
  1412. options = options || {};
  1413. // invalidate
  1414. if ( !options[target] && !targetData ) { return; }
  1415. // event targets, constants
  1416. var self = this, spyTarget = options[target] && queryElement(options[target]) || targetData,
  1417. links = spyTarget && spyTarget[getElementsByTagName]('A'),
  1418. offset = parseInt(options['offset'] || offsetData) || 10,
  1419. items = [], targetItems = [], scrollOffset,
  1420. scrollTarget = element[offsetHeight] < element[scrollHeight] ? element : globalObject, // determine which is the real scrollTarget
  1421. isWindow = scrollTarget === globalObject;
  1422. // populate items and targets
  1423. for (var i=0, il=links[length]; i<il; i++) {
  1424. var href = links[i][getAttribute]('href'),
  1425. targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' && queryElement(href);
  1426. if ( !!targetItem ) {
  1427. items[push](links[i]);
  1428. targetItems[push](targetItem);
  1429. }
  1430. }
  1431. // private methods
  1432. var updateItem = function(index) {
  1433. var parent = items[index][parentNode], // item's parent LI element
  1434. targetItem = targetItems[index], // the menu item targets this element
  1435. dropdown = getClosest(parent,'.dropdown'),
  1436. targetRect = isWindow && targetItem[getBoundingClientRect](),
  1437. isActive = hasClass(parent,active) || false,
  1438. topEdge = (isWindow ? targetRect[top] + scrollOffset : targetItem[offsetTop]) - offset,
  1439. bottomEdge = isWindow ? targetRect[bottom] + scrollOffset - offset : targetItems[index+1] ? targetItems[index+1][offsetTop] - offset : element[scrollHeight],
  1440. inside = scrollOffset >= topEdge && bottomEdge > scrollOffset;
  1441. if ( !isActive && inside ) {
  1442. if ( parent.tagName === 'LI' && !hasClass(parent,active) ) {
  1443. addClass(parent,active);
  1444. if (dropdown && !hasClass(dropdown,active) ) {
  1445. addClass(dropdown,active);
  1446. }
  1447. bootstrapCustomEvent.call(element, 'activate', 'scrollspy', items[index]);
  1448. }
  1449. } else if ( !inside ) {
  1450. if ( parent.tagName === 'LI' && hasClass(parent,active) ) {
  1451. removeClass(parent,active);
  1452. if (dropdown && hasClass(dropdown,active) && !getElementsByClassName(parent[parentNode],active).length ) {
  1453. removeClass(dropdown,active);
  1454. }
  1455. }
  1456. } else if ( !inside && !isActive || isActive && inside ) {
  1457. return;
  1458. }
  1459. },
  1460. updateItems = function(){
  1461. scrollOffset = isWindow ? getScroll().y : element[scrollTop];
  1462. for (var index=0, itl=items[length]; index<itl; index++) {
  1463. updateItem(index)
  1464. }
  1465. };
  1466. // public method
  1467. this.refresh = function () {
  1468. updateItems();
  1469. }
  1470. // init
  1471. if ( !(stringScrollSpy in element) ) { // prevent adding event handlers twice
  1472. on( scrollTarget, scrollEvent, self.refresh, passiveHandler );
  1473. !isIE8 && on( globalObject, resizeEvent, self.refresh, passiveHandler );
  1474. }
  1475. self.refresh();
  1476. element[stringScrollSpy] = self;
  1477. };
  1478. // SCROLLSPY DATA API
  1479. // ==================
  1480. supports[push]( [ stringScrollSpy, ScrollSpy, '['+dataSpy+'="scroll"]' ] );
  1481. /* Native Javascript for Bootstrap 3 | Tab
  1482. -----------------------------------------*/
  1483. // TAB DEFINITION
  1484. // ==============
  1485. var Tab = function( element, options ) {
  1486. // initialization element
  1487. element = queryElement(element);
  1488. // DATA API
  1489. var heightData = element[getAttribute](dataHeight),
  1490. // strings
  1491. component = 'tab', height = 'height', float = 'float', isAnimating = 'isAnimating';
  1492. // set options
  1493. options = options || {};
  1494. this[height] = supportTransitions ? (options[height] || heightData === 'true') : false; // filter legacy browsers
  1495. // bind, event targets
  1496. var self = this, next,
  1497. tabs = getClosest(element,'.nav'),
  1498. tabsContentContainer = false,
  1499. dropdown = tabs && queryElement('.dropdown',tabs),
  1500. activeTab, activeContent, nextContent, containerHeight, equalContents, nextHeight,
  1501. // trigger
  1502. triggerEnd = function(){
  1503. tabsContentContainer[style][height] = '';
  1504. removeClass(tabsContentContainer,collapsing);
  1505. tabs[isAnimating] = false;
  1506. },
  1507. triggerShow = function() {
  1508. if (tabsContentContainer) { // height animation
  1509. if ( equalContents ) {
  1510. triggerEnd();
  1511. } else {
  1512. setTimeout(function(){ // enables height animation
  1513. tabsContentContainer[style][height] = nextHeight + 'px'; // height animation
  1514. tabsContentContainer[offsetWidth];
  1515. emulateTransitionEnd(tabsContentContainer, triggerEnd);
  1516. },50);
  1517. }
  1518. } else {
  1519. tabs[isAnimating] = false;
  1520. }
  1521. bootstrapCustomEvent.call(next, shownEvent, component, activeTab);
  1522. },
  1523. triggerHide = function() {
  1524. if (tabsContentContainer) {
  1525. activeContent[style][float] = left;
  1526. nextContent[style][float] = left;
  1527. containerHeight = activeContent[scrollHeight];
  1528. }
  1529. addClass(nextContent,active);
  1530. bootstrapCustomEvent.call(next, showEvent, component, activeTab);
  1531. removeClass(activeContent,active);
  1532. bootstrapCustomEvent.call(activeTab, hiddenEvent, component, next);
  1533. if (tabsContentContainer) {
  1534. nextHeight = nextContent[scrollHeight];
  1535. equalContents = nextHeight === containerHeight;
  1536. addClass(tabsContentContainer,collapsing);
  1537. tabsContentContainer[style][height] = containerHeight + 'px'; // height animation
  1538. tabsContentContainer[offsetHeight];
  1539. activeContent[style][float] = '';
  1540. nextContent[style][float] = '';
  1541. }
  1542. if ( hasClass(nextContent, 'fade') ) {
  1543. setTimeout(function(){
  1544. addClass(nextContent,inClass);
  1545. emulateTransitionEnd(nextContent,triggerShow);
  1546. },20);
  1547. } else { triggerShow(); }
  1548. };
  1549. if (!tabs) return; // invalidate
  1550. // set default animation state
  1551. tabs[isAnimating] = false;
  1552. // private methods
  1553. var getActiveTab = function() {
  1554. var activeTabs = getElementsByClassName(tabs,active), activeTab;
  1555. if ( activeTabs[length] === 1 && !hasClass(activeTabs[0],'dropdown') ) {
  1556. activeTab = activeTabs[0];
  1557. } else if ( activeTabs[length] > 1 ) {
  1558. activeTab = activeTabs[activeTabs[length]-1];
  1559. }
  1560. return activeTab[getElementsByTagName]('A')[0];
  1561. },
  1562. getActiveContent = function() {
  1563. return queryElement(getActiveTab()[getAttribute]('href'));
  1564. },
  1565. // handler
  1566. clickHandler = function(e) {
  1567. e[preventDefault]();
  1568. next = e[currentTarget] || this; // IE8 needs to know who really currentTarget is
  1569. !tabs[isAnimating] && !hasClass(next[parentNode],active) && self.show();
  1570. };
  1571. // public method
  1572. this.show = function() { // the tab we clicked is now the next tab
  1573. next = next || element;
  1574. nextContent = queryElement(next[getAttribute]('href')); //this is the actual object, the next tab content to activate
  1575. activeTab = getActiveTab();
  1576. activeContent = getActiveContent();
  1577. tabs[isAnimating] = true;
  1578. removeClass(activeTab[parentNode],active);
  1579. activeTab[setAttribute](ariaExpanded,'false');
  1580. addClass(next[parentNode],active);
  1581. next[setAttribute](ariaExpanded,'true');
  1582. if ( dropdown ) {
  1583. if ( !hasClass(element[parentNode][parentNode],'dropdown-menu') ) {
  1584. if (hasClass(dropdown,active)) removeClass(dropdown,active);
  1585. } else {
  1586. if (!hasClass(dropdown,active)) addClass(dropdown,active);
  1587. }
  1588. }
  1589. bootstrapCustomEvent.call(activeTab, hideEvent, component, next);
  1590. if (hasClass(activeContent, 'fade')) {
  1591. removeClass(activeContent,inClass);
  1592. emulateTransitionEnd(activeContent, triggerHide);
  1593. } else { triggerHide(); }
  1594. };
  1595. // init
  1596. if ( !(stringTab in element) ) { // prevent adding event handlers twice
  1597. on(element, clickEvent, clickHandler);
  1598. }
  1599. if (self[height]) { tabsContentContainer = getActiveContent()[parentNode]; }
  1600. element[stringTab] = self;
  1601. };
  1602. // TAB DATA API
  1603. // ============
  1604. supports[push]( [ stringTab, Tab, '['+dataToggle+'="tab"]' ] );
  1605. /* Native Javascript for Bootstrap 3 | Tooltip
  1606. ---------------------------------------------*/
  1607. // TOOLTIP DEFINITION
  1608. // ==================
  1609. var Tooltip = function( element,options ) {
  1610. // initialization element
  1611. element = queryElement(element);
  1612. // set options
  1613. options = options || {};
  1614. // DATA API
  1615. var animationData = element[getAttribute](dataAnimation),
  1616. placementData = element[getAttribute](dataPlacement),
  1617. delayData = element[getAttribute](dataDelay),
  1618. containerData = element[getAttribute](dataContainer),
  1619. // strings
  1620. component = 'tooltip',
  1621. classString = 'class',
  1622. title = 'title',
  1623. fade = 'fade',
  1624. div = 'div',
  1625. // check container
  1626. containerElement = queryElement(options[container]),
  1627. containerDataElement = queryElement(containerData),
  1628. // maybe the element is inside a modal
  1629. modal = getClosest(element,'.modal'),
  1630. // maybe the element is inside a fixed navbar
  1631. navbarFixedTop = getClosest(element,'.'+fixedTop),
  1632. navbarFixedBottom = getClosest(element,'.'+fixedBottom);
  1633. // set instance options
  1634. this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade;
  1635. this[placement] = options[placement] ? options[placement] : placementData || top;
  1636. this[delay] = parseInt(options[delay] || delayData) || 200;
  1637. this[container] = containerElement ? containerElement
  1638. : containerDataElement ? containerDataElement
  1639. : navbarFixedTop ? navbarFixedTop
  1640. : navbarFixedBottom ? navbarFixedBottom
  1641. : modal ? modal : DOC[body];
  1642. // bind, event targets, title and constants
  1643. var self = this, timer = 0, placementSetting = this[placement], tooltip = null,
  1644. titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle);
  1645. if ( !titleString || titleString == "" ) return; // invalidate
  1646. // private methods
  1647. var removeToolTip = function() {
  1648. self[container].removeChild(tooltip);
  1649. tooltip = null; timer = null;
  1650. },
  1651. createToolTip = function() {
  1652. titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle); // read the title again
  1653. if ( !titleString || titleString == "" ) return false; // invalidate
  1654. tooltip = DOC[createElement](div);
  1655. tooltip[setAttribute]('role',component);
  1656. var tooltipArrow = DOC[createElement](div), tooltipInner = DOC[createElement](div);
  1657. tooltipArrow[setAttribute](classString, component+'-arrow'); tooltipInner[setAttribute](classString,component+'-inner');
  1658. tooltip[appendChild](tooltipArrow); tooltip[appendChild](tooltipInner);
  1659. tooltipInner[innerHTML] = titleString;
  1660. self[container][appendChild](tooltip);
  1661. tooltip[setAttribute](classString, component + ' ' + placementSetting + ' ' + self[animation]);
  1662. },
  1663. updateTooltip = function () {
  1664. styleTip(element,tooltip,placementSetting,self[container]);
  1665. },
  1666. showTooltip = function () {
  1667. !hasClass(tooltip,inClass) && ( addClass(tooltip,inClass) );
  1668. },
  1669. // triggers
  1670. showTrigger = function() {
  1671. bootstrapCustomEvent.call(element, shownEvent, component);
  1672. !isIE8 && on( globalObject, resizeEvent, self.hide, passiveHandler );
  1673. },
  1674. hideTrigger = function() {
  1675. !isIE8 && off( globalObject, resizeEvent, self.hide, passiveHandler );
  1676. removeToolTip();
  1677. bootstrapCustomEvent.call(element, hiddenEvent, component);
  1678. };
  1679. // public methods
  1680. this.show = function() {
  1681. clearTimeout(timer);
  1682. timer = setTimeout( function() {
  1683. if (tooltip === null) {
  1684. placementSetting = self[placement]; // we reset placement in all cases
  1685. if(createToolTip() == false) return;
  1686. updateTooltip();
  1687. showTooltip();
  1688. bootstrapCustomEvent.call(element, showEvent, component);
  1689. !!self[animation] ? emulateTransitionEnd(tooltip, showTrigger) : showTrigger();
  1690. }
  1691. }, 20 );
  1692. };
  1693. this.hide = function() {
  1694. clearTimeout(timer);
  1695. timer = setTimeout( function() {
  1696. if (tooltip && hasClass(tooltip,inClass)) {
  1697. bootstrapCustomEvent.call(element, hideEvent, component);
  1698. removeClass(tooltip,inClass);
  1699. !!self[animation] ? emulateTransitionEnd(tooltip, hideTrigger) : hideTrigger();
  1700. }
  1701. }, self[delay]);
  1702. };
  1703. this.toggle = function() {
  1704. if (!tooltip) { self.show(); }
  1705. else { self.hide(); }
  1706. };
  1707. // init
  1708. if ( !(stringTooltip in element) ) { // prevent adding event handlers twice
  1709. element[setAttribute](dataOriginalTitle,titleString);
  1710. element.removeAttribute(title);
  1711. on(element, mouseHover[0], self.show);
  1712. on(element, mouseHover[1], self.hide);
  1713. }
  1714. element[stringTooltip] = self;
  1715. };
  1716. // TOOLTIP DATA API
  1717. // =================
  1718. supports[push]( [ stringTooltip, Tooltip, '['+dataToggle+'="tooltip"]' ] );
  1719. /* Native Javascript for Bootstrap | Initialize Data API
  1720. --------------------------------------------------------*/
  1721. var initializeDataAPI = function( constructor, collection ){
  1722. for (var i=0, l=collection[length]; i<l; i++) {
  1723. new constructor(collection[i]);
  1724. }
  1725. },
  1726. initCallback = BSN.initCallback = function(lookUp){
  1727. lookUp = lookUp || DOC;
  1728. for (var i=0, l=supports[length]; i<l; i++) {
  1729. initializeDataAPI( supports[i][1], lookUp[querySelectorAll] (supports[i][2]) );
  1730. }
  1731. };
  1732. // bulk initialize all components
  1733. DOC[body] ? initCallback() : on( DOC, 'DOMContentLoaded', function(){ initCallback(); } );
  1734. return {
  1735. Affix: Affix,
  1736. Alert: Alert,
  1737. Button: Button,
  1738. Carousel: Carousel,
  1739. Collapse: Collapse,
  1740. Dropdown: Dropdown,
  1741. Modal: Modal,
  1742. Popover: Popover,
  1743. ScrollSpy: ScrollSpy,
  1744. Tab: Tab,
  1745. Tooltip: Tooltip
  1746. };
  1747. }));