/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * Copyright 2002-2019, Carrot Search s.c, All Rights Reserved. * * * A simple utility for attaching showing and hiding a * data loading and preparation progress indicator. The utility * will create and attach all the necessary HTML to the page. * * Please see demos/loading.html for the usage example. * * @param foamtree the FoamTree instance whose loading and * layout preparation progress should be monitored * @param indicatorHtml the HTML representing the loading message */ window.CarrotSearchFoamTree.loader = function(foamtree, indicatorHtml) { var deferDataObjectChange = false, dataObjectToSet = undefined; var dataObjectToSetProvided = false; // Duration of the loading element CSS transition. You may // need to modify this value if you modified the default CSS transitions. var duration = 350; // On Webkit CSS transitions seem to continue running during JS processing. // Other browsers need to wait for the transition to complete. var waitForTransition = !/chrome|webkit/.test(navigator.userAgent.toLowerCase()); // Create the indicator element var indicator = document.createElement("div"); indicator.className = "visualization-loading fadeout"; indicator.innerHTML = indicatorHtml; foamtree.get("element").appendChild(indicator); // Hide the indicator when rollout starts foamtree.on("rolloutStart", hideIndicator); return { /** * Call this method before data loading is initiated to * show the loading indicator. * * If FoamTree is set to perform a pullback animation, * the loading indicator will show after pullback is complete. */ started: function() { dataObjectToSetProvided = false; if (isPullbackEnabled()) { // We'll defer setting of the new data object // until pullback completes. FoamTree does that // internally too, but we need to show the indicator // before we set the new data object. If we didn't // do that, the element would actually show after // the diagram computation completes, which wouldn't // make much sense. deferDataObjectChange = true; once("modelChanged", showIndicatorAndSetDataObject); // Set an empty data object to trigger a pullback foamtree.set("dataObject", null); } else { deferDataObjectChange = false; showIndicator(); } }, /** * Call this method when data loading completes. This method * will take care of setting the new data object on FoamTree * and hiding the indicator when the diagram is ready to show. * * @param dataObject new data object to set */ complete: function (dataObject) { dataObjectToSetProvided = true; if (deferDataObjectChange) { // Just remember the new data object, we'll set it // after the pullback completes and the loading indicator // is made visible. dataObjectToSet = dataObject; } else { // Defer the update to give the browser a chance // to show the loading indicator. setTimeout(function() { foamtree.set("dataObject", dataObject); hideIndicatorIfDataObjectEmpty(dataObjectToSet); }, 1 + (waitForTransition ? duration + 50 : 1)); } } }; function showIndicatorAndSetDataObject() { showIndicator(); if (dataObjectToSetProvided && deferDataObjectChange) { // Defer the update to give the browser a chance // to show the loading indicator. setTimeout(function() { foamtree.set("dataObject", dataObjectToSet); hideIndicatorIfDataObjectEmpty(dataObjectToSet); }, 5 + (waitForTransition ? duration + 50 : 1)); } deferDataObjectChange = false; } function hideIndicatorIfDataObjectEmpty(dataObject) { if (!dataObject || !dataObject.groups || dataObject.groups.length == 0) { hideIndicator(); } } function hideIndicator() { addClass(indicator, "fadeout"); } function showIndicator() { // It would be good to defer showing of the indicator a bit // so that it's not shown when the loading process completes // very quickly. However, it's not currently possible to implement // because FoamTree diagram computation is performed in one // go and blocks until the computation completes. In the future, // it would be good to split the computation into chunks between // which FoamTree would yield to the UI thread to redraw the // DOM updates. removeClass(indicator, "fadeout"); } function addClass(element, clazz) { var classes = element.className.split(/\s+/); if (classes.indexOf(clazz) < 0) { classes.push(clazz); element.className = classes.join(" "); } } function removeClass(element, clazz) { element.className = element.className.split(/\s+/).filter(function (c) { return c != clazz; }).join(" "); } function isPullbackEnabled() { return foamtree.get("pullbackDuration") > 0 || foamtree.get("fadeDuration") > 0; } function once(event, fn) { foamtree.on(event, function() { foamtree.off(event); fn(); }); } };