diff --git a/dist/tuicss.js b/dist/tuicss.js index e44e9cd..4950a08 100644 --- a/dist/tuicss.js +++ b/dist/tuicss.js @@ -1,78 +1,167 @@ /** - * Init + * Replacement for jQuery's $(document).ready() function. + * This is handy in making sure stuff fires after the DOM is ready to be touched. + * Stolen from:https://stackoverflow.com/a/53601942/344028 + * + * @param fn Callback. */ -$(document).ready(function() { - tabsController(); - datetimeController(); - sidenavController(); - modalController(); -}); +function domReady(fn) { + // If we're early to the party + document.addEventListener('DOMContentLoaded', fn); + + // If late; I mean on time. + if (document.readyState === 'interactive' || document.readyState === 'complete') { + fn(); + } +} /** * TuiTabs controller */ function tabsController() { - $(".tui-tab").click(function(event) { - $(".tui-tab-content").hide(); - let tabContentid = $(this).attr('data-tab-content'); - $(`#${tabContentid}`).show(); - $(".tui-tab").removeClass("active"); - $(this).addClass("active"); - }); - $('.tui-tab.active').click(); + // Get all the tab elements (typically li tags). + const tabs = document.getElementsByClassName('tui-tab'); + + if (!tabs.length) { + // No tabs found, return early and save a couple CPU cycles. + return; + } + + for (const tab of tabs) { + // Add click listeners to them. + tab.addEventListener('click', function (e) { + // Remove the 'active' class from any and all tabs. + for (const otherTab of tabs) { + otherTab.classList.remove('active'); + } + + // Get the content element. + const tabContents = document.getElementsByClassName('tui-tab-content'); + + if (tabContents) { + for (const tabContent of tabContents) { + // Hide all tab contents. + tabContent.style.display = 'none'; + } + } else { + throw 'No tab content elements found.' + } + + // Get the id of the tab contents we want to show. + const tabContentId = e.target.getAttribute('data-tab-content'); + + if (tabContentId) { + const tabContent = document.getElementById(tabContentId); + if (tabContent) { + // Show the tab contents. + tabContent.style.display = 'block'; + } else { + throw 'No tab content element with id "' + tabContentId + '" found.'; + } + } + // We are not going to throw an error here, since we could make the tab do something else that a tab + // normally wouldn't do. + + // Set the clicked tab to have the 'active' class so we can use it in the next part. + e.target.classList.add('active'); + + }); + } + + // Grab the first tab with the active class. + const activeTab = document.querySelector('.tui-tab.active'); + if (activeTab) { + // Now click it 'click' it. + activeTab.click(); + } else { + // Nothing found, just click the first tab foud. + tabs[0].click() + } } /** - * Date field controller + * Date/time field controller */ function datetimeController() { + // Get date/time elements. + const clocks = document.getElementsByClassName('tui-datetime'); - if(!$(".tui-datetime").length) return; + if (!clocks.length) { + // No date time elements found, return early and save a couple CPU cycles. + return; + } + // Kick off our clock interval stuff. datetimeInterval(); setInterval(datetimeInterval, 1000); function datetimeInterval() { - let today = new Date(); + for (const clock of clocks) { + // Set the interval. + const interval = setInterval(() => { + // Technically we should have already returned earlier in the code, but to be on the safe side. + if (clock === null) { + clearInterval(interval); + return; + } - $(".tui-datetime").each(function() { - let clock = $(this); - - let format = clock.attr("data-format"); + // Get the format we want to display in the element. + let format = clock.getAttribute('data-format'); - let month = (today.getMonth() + "").length == 2 ? today.getMonth() + 1 : "0" + (today.getMonth() + 1); - let day = (today.getDay() + "").length == 2 ? today.getDay() + 1 : "0" + (today.getDay() + 1); - let year = today.getFullYear(); - let hour = (today.getHours() + "").length == 2 ? today.getHours() : "0" + today.getHours(); - let hour12 = (parseInt(hour) + 24) % 12 || 12; - let minute = (today.getMinutes() + "").length == 2 ? today.getMinutes() : "0" + today.getMinutes(); - let second = (today.getSeconds() + "").length == 2 ? today.getSeconds() : "0" + today.getSeconds(); - let ampm = parseInt(hour) >= 12 ? "PM" : "AM"; + // parse out the date and time into constants. + const today = new Date(); + const month = (today.getMonth() + '').length === 2 ? today.getMonth() + 1 : '0' + (today.getMonth() + 1); + const day = (today.getDay() + '').length === 2 ? today.getDay() + 1 : '0' + (today.getDay() + 1); + const year = today.getFullYear() + ''; + const hour = (today.getHours() + '').length === 2 ? today.getHours() : '0' + today.getHours(); + const hour12 = (parseInt(hour) + 24) % '12' || '12'; + const minute = (today.getMinutes() + '').length === 2 ? today.getMinutes() : '0' + today.getMinutes(); + const second = (today.getSeconds() + '').length === 2 ? today.getSeconds() : '0' + today.getSeconds(); + const ampm = parseInt(hour) >= 12 ? 'PM' : 'AM'; - format = format.replace("M", month); - format = format.replace("d", day); - format = format.replace("y", year); - format = format.replace("H", hour); - format = format.replace("h", hour12); - format = format.replace("m", minute); - format = format.replace("s", second); - format = format.replace("a", ampm); + // Replace based on the format. + format = format.replace('M', month); + format = format.replace('d', day); + format = format.replace('y', year); + format = format.replace('H', hour); + format = format.replace('h', hour12); + format = format.replace('m', minute); + format = format.replace('s', second); + format = format.replace('a', ampm); - clock.html(format); - }); + // Show it in the element. + clock.innerHTML = format; + }); + } } } /** * Sidenav Controller + * There should only side navigation element at the moment. */ function sidenavController() { - $(".tui-sidenav-button").click(function() { - let sidenav = $(".tui-sidenav"); - if(sidenav.hasClass("active")) { - $(".tui-sidenav").removeClass("active"); + // Get the side navigation button (there should be only one, but if not, we are getting the first one). + const sideNavButton = document.querySelector('.tui-sidenav-button'); + + if (!sideNavButton) { + // No side navigation button found, return early and save a couple CPU cycles. + return; + } + + // Add the click event listener to the buttons. + sideNavButton.addEventListener('click', () => { + // Get the side navigation element (there should be only one, but if not, we are getting the first one). + const sideNav = document.querySelector('.tui-sidenav'); + + if (sideNav) { + if (sideNav.classList.contains('active')) { + sideNav.classList.remove('active'); + } else { + sideNav.classList.add('active'); + } } else { - $(".tui-sidenav").addClass("active"); + throw 'No sidenav element found.' } }); } @@ -81,14 +170,80 @@ function sidenavController() { * Modal controller */ function modalController() { - $(".tui-modal-button").click(function() { - $(".tui-overlap").addClass("active"); - let modalId = $(this).attr("data-modal"); - $(`#${modalId}`).addClass("active"); - }); - $(".tui-modal-close-button").click(function() { - $(".tui-overlap").removeClass("active"); - let modalId = $(this).attr("data-modal"); - $(`#${modalId}`).removeClass("active"); - }); -} \ No newline at end of file + // Get the overlap (overlay) element (there should be only one, but if not, we are getting the first one). + const tuiOverlap = document.querySelector('.tui-overlap'); + + if (!tuiOverlap) { + // No overlap found element, return early and save a couple CPU cycles. + return; + } + + // Find modal buttons. + const modalButtons = document.getElementsByClassName('tui-modal-button'); + for (const modalButton of modalButtons) { + // Add the click event listener to the buttons. + modalButton.addEventListener('click', (e) => { + // Show the overlap. + tuiOverlap.classList.add('active'); + + // Get the display element for the modal. + const modalId = e.target.getAttribute('data-modal'); + + if (modalId) { + const modal = document.getElementById(modalId); + + if (modal) { + // Show it. + modal.classList.add('active'); + } else { + throw 'No modal element with id of "' + modalId + '" found.'; + } + } else { + throw 'Modal close button data-modal attribute is empty or not set.' + } + }); + } + + // Find the close modal buttons. + const modalCloseButtons = document.getElementsByClassName('tui-modal-close-button'); + + if (modalButtons.length > 0 && !modalCloseButtons.length) { + // A modal without a close button, is a bad modal. + throw 'No modal close buttons found.' + } + + for (const modalCloseButton of modalCloseButtons) { + // Add the click event listener to the buttons. + modalCloseButton.addEventListener('click', (e) => { + // Hide the the overlap. + tuiOverlap.classList.remove('active'); + + // Get the display element id for the modal. + const modalId = e.target.getAttribute('data-modal'); + + if (modalId) { + // Get the modal element. + const modal = document.getElementById(modalId); + + if (modal) { + // Hide it. + modal.classList.remove('active'); + } else { + throw 'No modal element with id of "' + modalId + '" found.'; + } + } else { + throw 'Modal close button data-modal attribute is empty or not set.' + } + }); + } +} + +/** + * Init: This is at the bottom to make sure it is fired correctly. + */ +domReady(function () { + tabsController(); + datetimeController(); + sidenavController(); + modalController(); +}); diff --git a/dist/tuicss.min.js b/dist/tuicss.min.js index c2a69f1..b6cccd1 100644 --- a/dist/tuicss.min.js +++ b/dist/tuicss.min.js @@ -1 +1 @@ -function tabsController(){$(".tui-tab").click(function(t){$(".tui-tab-content").hide();let e=$(this).attr("data-tab-content");$(`#${e}`).show(),$(".tui-tab").removeClass("active"),$(this).addClass("active")}),$(".tui-tab.active").click()}function datetimeController(){function t(){let t=new Date;$(".tui-datetime").each(function(){let e=$(this),a=e.attr("data-format"),n=2==(t.getMonth()+"").length?t.getMonth()+1:"0"+(t.getMonth()+1),l=2==(t.getDay()+"").length?t.getDay()+1:"0"+(t.getDay()+1),i=t.getFullYear(),o=2==(t.getHours()+"").length?t.getHours():"0"+t.getHours(),c=(parseInt(o)+24)%12||12,s=2==(t.getMinutes()+"").length?t.getMinutes():"0"+t.getMinutes(),r=2==(t.getSeconds()+"").length?t.getSeconds():"0"+t.getSeconds(),u=parseInt(o)>=12?"PM":"AM";a=(a=(a=(a=(a=(a=(a=(a=a.replace("M",n)).replace("d",l)).replace("y",i)).replace("H",o)).replace("h",c)).replace("m",s)).replace("s",r)).replace("a",u),e.html(a)})}$(".tui-datetime").length&&(t(),setInterval(t,1e3))}function sidenavController(){$(".tui-sidenav-button").click(function(){$(".tui-sidenav").hasClass("active")?$(".tui-sidenav").removeClass("active"):$(".tui-sidenav").addClass("active")})}function modalController(){$(".tui-modal-button").click(function(){$(".tui-overlap").addClass("active");let t=$(this).attr("data-modal");$(`#${t}`).addClass("active")}),$(".tui-modal-close-button").click(function(){$(".tui-overlap").removeClass("active");let t=$(this).attr("data-modal");$(`#${t}`).removeClass("active")})}$(document).ready(function(){tabsController(),datetimeController(),sidenavController(),modalController()}); \ No newline at end of file +function domReady(t){document.addEventListener("DOMContentLoaded",t),"interactive"!==document.readyState&&"complete"!==document.readyState||t()}function tabsController(){const t=document.getElementsByClassName("tui-tab");if(!t.length)return;for(const e of t)e.addEventListener("click",function(e){for(const e of t)e.classList.remove("active");const o=document.getElementsByClassName("tui-tab-content");if(!o)throw"No tab content elements found.";for(const t of o)t.style.display="none";const n=e.target.getAttribute("data-tab-content");if(n){const t=document.getElementById(n);if(!t)throw'No tab content element with id "'+n+'" found.';t.style.display="block"}e.target.classList.add("active")});const e=document.querySelector(".tui-tab.active");e?e.click():t[0].click()}function datetimeController(){const t=document.getElementsByClassName("tui-datetime");function e(){for(const e of t){const t=setInterval(()=>{if(null===e)return void clearInterval(t);let o=e.getAttribute("data-format");const n=new Date,a=2===(n.getMonth()+"").length?n.getMonth()+1:"0"+(n.getMonth()+1),s=2===(n.getDay()+"").length?n.getDay()+1:"0"+(n.getDay()+1),c=n.getFullYear()+"",l=2===(n.getHours()+"").length?n.getHours():"0"+n.getHours(),i=(parseInt(l)+24)%"12"||"12",r=2===(n.getMinutes()+"").length?n.getMinutes():"0"+n.getMinutes(),d=2===(n.getSeconds()+"").length?n.getSeconds():"0"+n.getSeconds(),u=parseInt(l)>=12?"PM":"AM";o=(o=(o=(o=(o=(o=(o=(o=o.replace("M",a)).replace("d",s)).replace("y",c)).replace("H",l)).replace("h",i)).replace("m",r)).replace("s",d)).replace("a",u),e.innerHTML=o})}}t.length&&(e(),setInterval(e,1e3))}function sidenavController(){const t=document.querySelector(".tui-sidenav-button");t&&t.addEventListener("click",()=>{const t=document.querySelector(".tui-sidenav");if(!t)throw"No sidenav element found.";t.classList.contains("active")?t.classList.remove("active"):t.classList.add("active")})}function modalController(){const t=document.querySelector(".tui-overlap");if(!t)return;const e=document.getElementsByClassName("tui-modal-button");for(const o of e)o.addEventListener("click",e=>{t.classList.add("active");const o=e.target.getAttribute("data-modal");if(!o)throw"Modal close button data-modal attribute is empty or not set.";{const t=document.getElementById(o);if(!t)throw'No modal element with id of "'+o+'" found.';t.classList.add("active")}});const o=document.getElementsByClassName("tui-modal-close-button");if(e.length>0&&!o.length)throw"No modal close buttons found.";for(const e of o)e.addEventListener("click",e=>{t.classList.remove("active");const o=e.target.getAttribute("data-modal");if(!o)throw"Modal close button data-modal attribute is empty or not set.";{const t=document.getElementById(o);if(!t)throw'No modal element with id of "'+o+'" found.';t.classList.remove("active")}})}domReady(function(){tabsController(),datetimeController(),sidenavController(),modalController()}); \ No newline at end of file diff --git a/src/js/tuicss.js b/src/js/tuicss.js index 92c2e25..4950a08 100644 --- a/src/js/tuicss.js +++ b/src/js/tuicss.js @@ -1,5 +1,6 @@ /** * Replacement for jQuery's $(document).ready() function. + * This is handy in making sure stuff fires after the DOM is ready to be touched. * Stolen from:https://stackoverflow.com/a/53601942/344028 * * @param fn Callback. @@ -18,36 +19,63 @@ function domReady(fn) { * TuiTabs controller */ function tabsController() { + // Get all the tab elements (typically li tags). const tabs = document.getElementsByClassName('tui-tab'); - for (const tab of tabs) { - tab.addEventListener('click', function (e) { - // Deactivate the tab. - tab.classList.remove('active'); + if (!tabs.length) { + // No tabs found, return early and save a couple CPU cycles. + return; + } + for (const tab of tabs) { + // Add click listeners to them. + tab.addEventListener('click', function (e) { + // Remove the 'active' class from any and all tabs. + for (const otherTab of tabs) { + otherTab.classList.remove('active'); + } + + // Get the content element. const tabContents = document.getElementsByClassName('tui-tab-content'); - for (const tabContent of tabContents) { - tabContent.style.display = 'none'; + if (tabContents) { + for (const tabContent of tabContents) { + // Hide all tab contents. + tabContent.style.display = 'none'; + } + } else { + throw 'No tab content elements found.' } + // Get the id of the tab contents we want to show. const tabContentId = e.target.getAttribute('data-tab-content'); - const tabContent = document.getElementById(tabContentId); - if (tabContent) { - tabContent.style.display = 'block'; - } - // Set the clicked tab to have the active click for the next part. + if (tabContentId) { + const tabContent = document.getElementById(tabContentId); + if (tabContent) { + // Show the tab contents. + tabContent.style.display = 'block'; + } else { + throw 'No tab content element with id "' + tabContentId + '" found.'; + } + } + // We are not going to throw an error here, since we could make the tab do something else that a tab + // normally wouldn't do. + + // Set the clicked tab to have the 'active' class so we can use it in the next part. e.target.classList.add('active'); + }); } - + // Grab the first tab with the active class. const activeTab = document.querySelector('.tui-tab.active'); - if (activeTab) { - // There can only be one active tab, so click the first found. + // Now click it 'click' it. activeTab.click(); + } else { + // Nothing found, just click the first tab foud. + tabs[0].click() } } @@ -55,26 +83,32 @@ function tabsController() { * Date/time field controller */ function datetimeController() { + // Get date/time elements. const clocks = document.getElementsByClassName('tui-datetime'); - // No elements found, return early. if (!clocks.length) { + // No date time elements found, return early and save a couple CPU cycles. return; } + // Kick off our clock interval stuff. datetimeInterval(); setInterval(datetimeInterval, 1000); function datetimeInterval() { for (const clock of clocks) { + // Set the interval. const interval = setInterval(() => { + // Technically we should have already returned earlier in the code, but to be on the safe side. if (clock === null) { clearInterval(interval); return; } + // Get the format we want to display in the element. let format = clock.getAttribute('data-format'); + // parse out the date and time into constants. const today = new Date(); const month = (today.getMonth() + '').length === 2 ? today.getMonth() + 1 : '0' + (today.getMonth() + 1); const day = (today.getDay() + '').length === 2 ? today.getDay() + 1 : '0' + (today.getDay() + 1); @@ -85,6 +119,7 @@ function datetimeController() { const second = (today.getSeconds() + '').length === 2 ? today.getSeconds() : '0' + today.getSeconds(); const ampm = parseInt(hour) >= 12 ? 'PM' : 'AM'; + // Replace based on the format. format = format.replace('M', month); format = format.replace('d', day); format = format.replace('y', year); @@ -94,6 +129,7 @@ function datetimeController() { format = format.replace('s', second); format = format.replace('a', ampm); + // Show it in the element. clock.innerHTML = format; }); } @@ -102,61 +138,108 @@ function datetimeController() { /** * Sidenav Controller + * There should only side navigation element at the moment. */ function sidenavController() { - const sideNavButtons = document.getElementsByClassName('tui-sidenav-button'); + // Get the side navigation button (there should be only one, but if not, we are getting the first one). + const sideNavButton = document.querySelector('.tui-sidenav-button'); - for (const sideNavButton of sideNavButtons) { - sideNavButton.addEventListener('click', () => { - const sideNavs = document.getElementsByClassName('tui-sidenav'); - for (const sideNav of sideNavs) { - if (sideNav.classList.contains('active')) { - sideNav.classList.remove('active'); - } else { - sideNav.classList.add('active'); - } - } - }); + if (!sideNavButton) { + // No side navigation button found, return early and save a couple CPU cycles. + return; } + + // Add the click event listener to the buttons. + sideNavButton.addEventListener('click', () => { + // Get the side navigation element (there should be only one, but if not, we are getting the first one). + const sideNav = document.querySelector('.tui-sidenav'); + + if (sideNav) { + if (sideNav.classList.contains('active')) { + sideNav.classList.remove('active'); + } else { + sideNav.classList.add('active'); + } + } else { + throw 'No sidenav element found.' + } + }); } /** * Modal controller */ function modalController() { - // Attach to the button that shows the modal. + // Get the overlap (overlay) element (there should be only one, but if not, we are getting the first one). + const tuiOverlap = document.querySelector('.tui-overlap'); + + if (!tuiOverlap) { + // No overlap found element, return early and save a couple CPU cycles. + return; + } + + // Find modal buttons. const modalButtons = document.getElementsByClassName('tui-modal-button'); for (const modalButton of modalButtons) { + // Add the click event listener to the buttons. modalButton.addEventListener('click', (e) => { - const tuiOverlaps = document.getElementsByClassName('tui-overlap'); - for (const tuiOverlap of tuiOverlaps) { - tuiOverlap.classList.add('active'); - } + // Show the overlap. + tuiOverlap.classList.add('active'); + // Get the display element for the modal. const modalId = e.target.getAttribute('data-modal'); - const modal = document.getElementById(modalId); - modal.classList.add('active'); + + if (modalId) { + const modal = document.getElementById(modalId); + + if (modal) { + // Show it. + modal.classList.add('active'); + } else { + throw 'No modal element with id of "' + modalId + '" found.'; + } + } else { + throw 'Modal close button data-modal attribute is empty or not set.' + } }); } - // Attach to the button that removes the modal. + // Find the close modal buttons. const modalCloseButtons = document.getElementsByClassName('tui-modal-close-button'); - for (const modalCloseButton of modalCloseButtons) { - modalCloseButton.addEventListener('click', (e) => { - const tuiOverlaps = document.getElementsByClassName('tui-overlap'); - for (const tuiOverlap of tuiOverlaps) { - tuiOverlap.classList.remove('active'); - } + if (modalButtons.length > 0 && !modalCloseButtons.length) { + // A modal without a close button, is a bad modal. + throw 'No modal close buttons found.' + } + + for (const modalCloseButton of modalCloseButtons) { + // Add the click event listener to the buttons. + modalCloseButton.addEventListener('click', (e) => { + // Hide the the overlap. + tuiOverlap.classList.remove('active'); + + // Get the display element id for the modal. const modalId = e.target.getAttribute('data-modal'); - const modal = document.getElementById(modalId); - modal.classList.remove('active'); + + if (modalId) { + // Get the modal element. + const modal = document.getElementById(modalId); + + if (modal) { + // Hide it. + modal.classList.remove('active'); + } else { + throw 'No modal element with id of "' + modalId + '" found.'; + } + } else { + throw 'Modal close button data-modal attribute is empty or not set.' + } }); } } /** - * Init: This is at the bottom to make sure the functions are parsed. It's a CYA. + * Init: This is at the bottom to make sure it is fired correctly. */ domReady(function () { tabsController();