Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
543 lines (478 sloc)
18.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| $(function () { | |
| // handles shinyapps.io | |
| var workerId = $('base').attr('href'); | |
| // ensure that this code does not locally | |
| if (typeof workerId != "undefined") { | |
| var hash = window.location.hash; | |
| var search = window.location.search; | |
| var pathname = window.location.pathname; | |
| var newpath = pathname + workerId + search + hash; | |
| window.history.replaceState( {} , 'newpath', newpath); | |
| } | |
| // From this we can recover the workerId and the sessionId. sessionId | |
| // is the same recovered on the server side with session$token. | |
| $(document).on('shiny:sessioninitialized', function(event) { | |
| Shiny.setInputValue('shinyInfo', Shiny.shinyapp.config); | |
| }); | |
| // Returns the last input changed (name, value, type, ...) | |
| $(document).on('shiny:inputchanged', function(event) { | |
| Shiny.setInputValue('lastInputChanged', {name: event.name, value: event.value, type: event.binding.name.split('.')[1]}); | |
| }); | |
| // Framework7.device is extremely useful to set up custom design | |
| $(document).on('shiny:connected', function(event) { | |
| Shiny.setInputValue('deviceInfo', Framework7.device); | |
| }); | |
| // handle toolbar padding for mobiles in standalone mode | |
| // only if there is an appbar | |
| if (Framework7.device.standalone) { | |
| $('html, body').css({'height':'100vh', 'width':'100vw'}); | |
| if ($('.appbar').length > 0) { | |
| $('.toolbar').css('margin-bottom', '20px'); | |
| } | |
| } | |
| // fix standalone tabs height issue | |
| $('.tabs-standalone').css('height', 'auto'); | |
| // Fix messagebar send icon issue when filled is TRUE. It is not | |
| // visible because it takes the same color has the messagebar background ... | |
| // To detect if the layout is filled, we search in the body class since the | |
| // global color is hosted here. | |
| if ($('body').attr('filled') === 'true' && !$('html').hasClass('theme-light') && $('body').attr('class') !== '#ffffff'){ | |
| $('.demo-send-message-link') | |
| .find('i') | |
| .addClass('color-white'); | |
| } | |
| // handle background for dark mode | |
| // need to remove the custom gainsboro color background | |
| var dark_mode = $('html').hasClass('theme-dark'); | |
| if (dark_mode) { | |
| $('.page-content').css('background-color', ''); | |
| $('.page-content.tab, .tab').css('background-color', ''); | |
| $('.demo-facebook-card .card-footer').css('background-color', '#1c1c1d'); | |
| $('.sheet-modal, .swipe-handler').css('background-color', '#1b1b1d'); | |
| $('.popup').css('background-color', '#1b1b1d'); | |
| $('.fab-label').css('background-color', 'var(--f7-fab-label-text-color)'); | |
| $('.fab-label').css('color', 'var(--f7-fab-text-color)'); | |
| // fix black accordion text in dark mode | |
| $('.accordion-item .item-content .item-inner').css('color', 'white'); | |
| $('.accordion-item .accordion-item-content').css('color', 'white'); | |
| // below the sidebar id #f7-sidebar-view ensures that we do not | |
| // screw up the classic f7Panel style in dark mode | |
| // The sidebar background has to be slightly lighter than the main background | |
| var sidebarPanel = $('#f7-sidebar-view').find('.page-content'); | |
| $(sidebarPanel).css('background-color', '#1e1e1e'); | |
| // we also need to darken sidebar items in the sidebar menu | |
| // the default color does not contrast enough with the | |
| // new sidebar background | |
| var sidebarItems = $('#f7-sidebar-view').find('li'); | |
| $(sidebarItems).css('background-color', '#171717'); | |
| } else { | |
| $('div.messages').css('background-color', 'gainsboro'); | |
| // fix photo browser links issue | |
| $('a').on('click', function() { | |
| setTimeout(function() { | |
| // we recover the body class that contains the page | |
| // color we set up in f7Init | |
| var linkColors = $('body').attr('class'); | |
| $('.navbar-photo-browser .navbar-inner .title').css('color', 'black'); | |
| $('.navbar-photo-browser .navbar-inner .right .popup-close').css('color', linkColors); | |
| $('.photo-browser-page .toolbar .toolbar-inner a').css('color', linkColors); | |
| }, 100); | |
| }); | |
| } | |
| // allow for subnavbar. If a subnavbar if provided in the navbar | |
| // add a custom class to the page so that the subnavbar is rendered | |
| var subnavbar = $('.subnavbar'); | |
| if (subnavbar.length == 1) { | |
| $('.page').addClass('page-with-subnavbar'); | |
| } | |
| // set up notifications | |
| // for now, it only works for 1 notification at a time | |
| Shiny.addCustomMessageHandler("notif", function(message) { | |
| // properly treat Booleans get from R | |
| // R returns strings containing 'true' or 'false' | |
| var closeButton = (message.closeButton == 'true'); | |
| var closeOnClick = (message.closeOnClick == 'true'); | |
| var swipeToClose = (message.swipeToClose == 'true'); | |
| // create the HTML icon | |
| var icon; | |
| if (message.icon !== undefined) { | |
| icon = '<i class="' + | |
| message.icon.attribs.class +'">' + | |
| message.icon.children[0] + '</i>'; | |
| } else { | |
| icon = undefined; | |
| } | |
| var notif = app.notification.create({ | |
| icon: icon, | |
| title: message.title, | |
| titleRightText: message.titleRightText, | |
| subtitle: message.subtitle, | |
| text: message.text, | |
| closeTimeout: parseInt(message.closeTimeout), | |
| closeOnClick: closeOnClick, | |
| swipeToClose: swipeToClose, | |
| closeButton: closeButton | |
| }); | |
| // Open Notifications | |
| notif.open(); | |
| }); | |
| // set up popovers | |
| popoverIds = []; | |
| getAllPopoverIds = function() { | |
| //In data-popover attribute we specify CSS selector of popover we need to open | |
| $('[data-popover]').each(function() { | |
| popoverIds.push($(this).attr("data-popover")); | |
| }); | |
| }; | |
| // call the function ... | |
| getAllPopoverIds(); | |
| popoverIds.forEach(function(index) { | |
| Shiny.addCustomMessageHandler(index, function(message) { | |
| var popover = app.popover.create({ | |
| targetEl: '[data-popover = "' + index + '"]', | |
| content: '<div class="popover">'+ | |
| '<div class="popover-inner">'+ | |
| '<div class="block">'+ | |
| message.content + | |
| '</div>'+ | |
| '</div>'+ | |
| '</div>', | |
| // Events | |
| on: { | |
| open: function (popover) { | |
| console.log('Popover open'); | |
| }, | |
| opened: function (popover) { | |
| console.log('Popover opened'); | |
| }, | |
| } | |
| }); | |
| $('[data-popover = "' + index + '"]').on('click', function() { | |
| popover.open(); | |
| }); | |
| }); | |
| }); | |
| // handle toasts | |
| Shiny.addCustomMessageHandler("toast", function(message) { | |
| // properly treat Booleans get from R | |
| // R returns strings containing 'true' or 'false' | |
| var closeButton = (message.closeButton == 'true'); | |
| var toast = app.toast.create({ | |
| icon: message.icon, | |
| text: message.text, | |
| position: message.position, | |
| closeButton: closeButton, | |
| closeTimeout: parseInt(message.closeTimeout), | |
| closeButtonText: message.closeButtonText, | |
| closeButtonColor: message.closeButtonColor | |
| }); | |
| // Open Notifications | |
| toast.open(); | |
| }); | |
| // handle dialog | |
| Shiny.addCustomMessageHandler("dialog", function(message) { | |
| var type = message.type; | |
| switch (type) { | |
| case 'alert': | |
| var dialog = app.dialog.alert(message.text, message.title); | |
| break; | |
| case 'confirm': | |
| var confirm = app.dialog.confirm( | |
| text = message.text, | |
| title = message.title, | |
| callbackOk = function() { | |
| Shiny.setInputValue(message.id, true); | |
| }, | |
| callbackCancel = function() { | |
| Shiny.setInputValue(message.id, false); | |
| } | |
| ).open(Shiny.setInputValue(message.id, null)); | |
| //confirm.closed(Shiny.setInputValue(message.id, null)); | |
| break; | |
| case 'prompt': | |
| var prompt = app.dialog.prompt( | |
| text = message.text, | |
| title = message.title, | |
| callbackOk = function(value) { | |
| Shiny.setInputValue(message.id, value); | |
| }, | |
| callbackCancel = function() { | |
| Shiny.setInputValue(message.id, null); | |
| } | |
| ).open(Shiny.setInputValue(message.id, null)); | |
| break; | |
| case 'login': | |
| console.log(login); | |
| var login = app.dialog.login( | |
| text = message.text, | |
| title = message.title, | |
| callbackOk = function (username, password) { | |
| Shiny.setInputValue(message.id, {user: username, password: password}); | |
| }, | |
| callbackCancel = function() { | |
| Shiny.setInputValue(message.id, null); | |
| } | |
| ).open(Shiny.setInputValue(message.id, null)); | |
| break; | |
| default: | |
| console.log(''); | |
| } | |
| }); | |
| // handle taphold events | |
| Shiny.addCustomMessageHandler('tapHold', function(message) { | |
| var selector = String(message.target); | |
| $(selector).on('taphold', function() { | |
| eval(message.callback); | |
| }); | |
| }); | |
| // handle f7InsertTab and f7RemoveTab ... | |
| // recover all tabSet ids in an array | |
| // The idea is that we will add each respective | |
| // id to the Shiny.addCustomMessageHandler function | |
| // which first argument is the type and should be the id | |
| // of the targeted tabSet | |
| var tabIds = []; | |
| getAllTabSetIds = function() { | |
| $('.tabs.ios-edges').each(function() { | |
| tabIds.push(this.id); | |
| }); | |
| }; | |
| // call the function ... | |
| getAllTabSetIds(); | |
| // f7InsertTab js | |
| tabIds.forEach(function(index) { | |
| var id = "insert_" + index; | |
| Shiny.addCustomMessageHandler(id, function(message) { | |
| var tabId = $("#" + message.ns + "-" + message.target); | |
| // for swipeable tabs | |
| var newTab; | |
| if ($(tabId).hasClass('swiper-slide')) { | |
| // prepare the new slide | |
| newTab = $(message.value).addClass('swiper-slide'); | |
| // remove page content class for standalone tabs | |
| if ($('.tabLinks').children(1).hasClass('segmented')) { | |
| $(newTab).removeClass('page-content'); | |
| } | |
| // add active if necessary | |
| if (message.select === "true") { | |
| $(newTab).addClass('swiper-slide-active'); | |
| } | |
| if (dark_mode) $(newTab).css('background-color', ''); | |
| } else { | |
| // remove white background for tab in dark mode | |
| newTab = $(message.value); | |
| if (dark_mode) $(newTab).css('background-color', ''); | |
| } | |
| if (message.position === "after") { | |
| // insert after the targeted tag in the tab-panel div | |
| $(newTab).insertAfter($(tabId)); | |
| // we also need to insert an item in the navigation | |
| $(message.link).insertAfter($('.tabLinks [href ="#' + message.ns + "-" + message.target + '"]')); | |
| } else if (message.position === "before") { | |
| // insert before the targeted tag in the tab-panel div | |
| $(newTab).insertBefore($(tabId)); | |
| // we also need to insert an item in the navigation | |
| $(message.link).insertBefore($('.tabLinks [href ="#' + message.ns + "-" + message.target + '"]')); | |
| } | |
| // we need to transform a in button in case | |
| // the container has segmented class (for standalone tabs). | |
| // This is ignored for toolbar tabs | |
| if ($('.tabLinks').children(1).hasClass('segmented')) { | |
| var newLink; | |
| var oldLink = $('.tabLinks [href ="#' + message.id + '"]'); | |
| newLink = $(oldLink) | |
| .replaceWith('<button class="button tab-link" href="#' + message.id + '">' + $(oldLink).html() + '</button>'); | |
| } | |
| // update the swiper if needed | |
| if ($(tabId).hasClass('swiper-slide')) { | |
| // access the swiper container | |
| var swiper = document.querySelector('.swiper-container').swiper; | |
| swiper.update(); | |
| } | |
| // if the newly inserted tab is active, disable other tabs | |
| if (message.select === "true") { | |
| // trigger a click on corresponding the new tab button. | |
| app.tab.show('#' + message.id, true); | |
| } | |
| }); | |
| }); | |
| // f7RemoveTab js | |
| tabIds.forEach(function(index) { | |
| var id = "remove_" + index; | |
| Shiny.addCustomMessageHandler(id, function(message) { | |
| // show the next tab first | |
| var tabToRemove = $('#' + message.ns + "-" + message.target); | |
| // important: prevent tab from translating which would lead to a | |
| // white screen | |
| $(".tabs.ios-edges").css('transform', ''); | |
| // remove the tab link: if condition to handle the case | |
| // of standalone tabs vs toolbar tabs | |
| if (!$('.tabLinks').children(1).hasClass('segmented')) { | |
| $('.toolbar-inner a[href="#' + message.ns + "-" + message.target +'"]').remove(); | |
| } else { | |
| var linkToRemove = $('.tabLinks button[href="#' + message.ns + "-" + message.target +'"]'); | |
| var otherLinks = $('.tabLinks button').not('[href="#' + message.ns + "-" + message.target +'"]'); | |
| if ($(linkToRemove).next().length === 0) { | |
| if (!$(otherLinks).hasClass('tab-link-active')) { | |
| $(linkToRemove).prev().addClass('tab-link-active'); | |
| } | |
| } else { | |
| if (!$(otherLinks).hasClass('tab-link-active')) { | |
| $(linkToRemove).next().addClass('tab-link-active'); | |
| } | |
| } | |
| $(linkToRemove).remove(); | |
| } | |
| // remove the tab body content | |
| $('#' + message.ns + "-" + message.target).remove(); | |
| // update the swiper if needed | |
| if ($(tabToRemove).hasClass('swiper-slide')) { | |
| // access the swiper container | |
| var swiper = document.querySelector('.swiper-container').swiper; | |
| swiper.update(); | |
| } | |
| // show the next element. Need to be after the swiper update. | |
| var nextTabId = $(tabToRemove).next().attr('id'); | |
| app.tab.show('#' + nextTabId); | |
| // we programmatically remove the old tabbar indicator and rebuild it. | |
| // The with of the tabbar indicator depends on the number of tab items it contains | |
| if (!$('.tabLinks').children(1).hasClass('segmented')) { | |
| $('.tab-link-highlight').remove(); | |
| segment_width = 100 / $('.toolbar-inner > a').length; | |
| $('.toolbar-inner').append('<span class="tab-link-highlight" style="width: ' + segment_width + '%;"></span>'); | |
| } | |
| }); | |
| }); | |
| // update f7Progress | |
| progressIds = []; | |
| getAllProgressIds = function() { | |
| $('.progressbar').each(function() { | |
| progressIds.push($(this).attr('id')); | |
| }); | |
| }; | |
| // call the function ... | |
| getAllProgressIds(); | |
| progressIds.forEach(function(index) { | |
| Shiny.addCustomMessageHandler(index, function(message) { | |
| app.progressbar.set('#' + index, message); | |
| }); | |
| }); | |
| // show navbar | |
| Shiny.addCustomMessageHandler('show_navbar', function(message) { | |
| var animate; | |
| if (message.animate == "true") animate = true; else animate = false; | |
| app.navbar.show('.navbar', animate = message.animate); | |
| }); | |
| // hide navbar | |
| Shiny.addCustomMessageHandler('hide_navbar', function(message) { | |
| var animate; var hideStatusbar; | |
| if (message.animate == "true") animate = true; else animate = false; | |
| if (message.hideStatusbar == "true") hideStatusbar = true; else hideStatusbar = false; | |
| app.navbar.hide( | |
| '.navbar', | |
| animate = animate, | |
| hideStatusbar = hideStatusbar | |
| ); | |
| }); | |
| // handle action sheet | |
| Shiny.addCustomMessageHandler('action-sheet', function(message) { | |
| var grid; | |
| var buttonsId = message.id + '_button'; | |
| if (message.grid == "true") grid = true; else grid = false; | |
| // define function that set an inputvalue those name depends on an index | |
| // parameter | |
| function setButtonInput(index) { Shiny.setInputValue(buttonsId, index) } | |
| // add those functions to the message.button array | |
| function setOnClick(element, index) { | |
| Object.defineProperty(element, 'onClick', { | |
| value: function() {setButtonInput(index + 1);}, | |
| writable: false | |
| }); | |
| } | |
| message.buttons.forEach(setOnClick); | |
| // create the sheet | |
| var actionSheet = app.actions.create({ | |
| //el: message.id, | |
| grid: grid, | |
| buttons: message.buttons, | |
| // below we set up events to set/update input values for Shiny | |
| on: { | |
| opened: function () { | |
| Shiny.setInputValue(message.id, true); | |
| }, | |
| closed: function () { | |
| Shiny.setInputValue(message.id, false); | |
| // input$button is null when the action is closed | |
| Shiny.setInputValue(buttonsId, null); | |
| } | |
| } | |
| }); | |
| // open the sheet | |
| actionSheet.open(); | |
| }); | |
| // pull to refresh | |
| // add the preoloader tag dynamically | |
| // We use the App root data feature | |
| if (app.data.pullToRefresh) { | |
| const ptrLoader = $( | |
| '<div class="ptr-preloader">' + | |
| '<div class="preloader"></div>' + | |
| '<div class="ptr-arrow"></div>' + | |
| '</div>' | |
| ); | |
| // add useful classes to the page content | |
| $('.page-content') | |
| .addClass('ptr-content') | |
| .prepend(ptrLoader) | |
| .attr('data-ptr-distance', '55') | |
| .attr('data-ptr-mousewheel', 'true'); | |
| // we need to create the ptr manually since it | |
| // is added after the page initialization | |
| app.ptr.create('.ptr-content'); | |
| var ptr = app.ptr.get('.ptr-content'); | |
| // Add 'refresh' listener on it | |
| ptr.on('refresh', function(e) { | |
| // Emulate 2s loading | |
| Shiny.setInputValue('ptr', true); | |
| setTimeout(function () { | |
| app.ptr.done(); | |
| }, 2000); | |
| }); | |
| // reset input. This will prevent observeEvent from | |
| // always being fired since they ignore NULL by default. | |
| // Therefore, instead of setting ptr to FALSE, we set it | |
| // to null | |
| ptr.on('done', function(e) { | |
| Shiny.setInputValue('ptr', null); | |
| }); | |
| } | |
| // validate inputs (see f7ValidateInput) | |
| Shiny.addCustomMessageHandler('validate-input', function(message) { | |
| $('#' + message.target) | |
| .attr('required','') | |
| .attr('validate', '') | |
| .attr('pattern', message.pattern) | |
| .attr('data-error-message', message.error); | |
| $('#' + message.target).closest('.item-content.item-input').addClass('item-input-with-info'); | |
| var infoTag; | |
| if (message.info !== undefined) { | |
| infoTag = '<div class = "item-input-info">' + message.info + '</div>'; | |
| } | |
| $('#' + message.target).parent().append(infoTag); | |
| }); | |
| // preloader | |
| Shiny.addCustomMessageHandler('show-preloader', function(message) { | |
| if (typeof message.el !== 'undefined') { | |
| app.preloader.showIn(message.el, message.color); | |
| } else { | |
| app.preloader.show(message.color); | |
| } | |
| }); | |
| Shiny.addCustomMessageHandler('hide-preloader', function(message) { | |
| if (typeof message.el !== 'undefined') { | |
| app.preloader.hideIn(message.el); | |
| } else { | |
| app.preloader.hide(); | |
| } | |
| }); | |
| }); |