Description |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Explore – 1</title> <style id="applicationStylesheet" type="text/css"> .mediaViewInfo { --web-view-name: Explore – 1; --web-view-id: Explore__1; --web-scale-on-resize: true; --web-enable-deep-linking: true; } :root { --web-view-ids: Explore__1; } * { margin: 0; padding: 0; box-sizing: border-box; border: none; } #Explore__1 { position: absolute; width: 1900px; height: 1080px; background-color: rgba(255,255,255,1); overflow: hidden; --web-view-name: Explore – 1; --web-view-id: Explore__1; --web-scale-on-resize: true; --web-enable-deep-linking: true; } #Scroll_Group_1 { mix-blend-mode: normal; position: absolute; width: 1920px; height: 1080px; left: 0px; top: 0px; overflow: hidden; } #robin-noguier-3pC6oFadbF8-unsp { position: absolute; width: 1920px; height: 1080px; left: 0px; top: 0px; overflow: visible; } #Rectangle_4 { filter: blur(30px); fill: transparent; } .Rectangle_4 { position: absolute; overflow: visible; width: 140px; height: 1080px; left: 0px; top: 0px; } #Travel { position: absolute; width: 466px; height: 407px; left: 220px; top: 252px; overflow: visible; } #Explore_Indonesia { left: 0px; top: 0px; position: absolute; overflow: visible; width: 467px; height: 218px; text-align: left; font-family: Open Sans; font-style: normal; font-weight: bold; font-size: 80px; color: rgba(255,255,255,1); text-transform: uppercase; } #Rectangle_10 { opacity: 0.28; fill: rgba(255,255,255,1); } .Rectangle_10 { position: absolute; overflow: visible; width: 398px; height: 124px; left: 3px; top: 283px; } #Start_Traveling { left: 29px; top: 311px; position: absolute; overflow: visible; width: 352px; height: 60px; text-align: center; font-family: Open Sans; font-style: normal; font-weight: bold; font-size: 44px; color: rgba(255,255,255,1); } #Barre { position: absolute; width: 37.83px; height: 522.461px; left: 55.085px; top: 217.5px; overflow: visible; } #Line_1 { fill: transparent; stroke: rgba(179,138,197,1); stroke-width: 6px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Line_1 { overflow: visible; position: absolute; width: 8.999px; height: 130.138px; left: 12.415px; top: 0px; transform: matrix(1,0,0,1,0,0); } #Line_2 { fill: transparent; stroke: rgba(255,255,255,1); stroke-width: 4px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Line_2 { overflow: visible; position: absolute; width: 7px; height: 304.04px; left: 15.415px; top: 130px; transform: matrix(1,0,0,1,0,0); } #n_1 { transform: translate(-55.085px, -217.5px) matrix(1,0,0,1,57,704) rotate(265deg); transform-origin: center; left: 0px; top: 0px; position: absolute; overflow: visible; width: 35px; height: 35px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 26px; color: rgba(179,138,197,1); } #Footer_000 { position: absolute; width: 1783px; height: 244px; left: 137px; top: 841px; overflow: visible; } #Rectangle_5 { opacity: 0.13; fill: rgba(255,255,255,1); stroke: rgba(255,20,20,1); stroke-width: 1px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Rectangle_5 { position: absolute; overflow: visible; width: 1779px; height: 210px; left: 3px; top: 34px; } #Rectangle_6 { opacity: 0.04; fill: rgba(255,255,255,1); } .Rectangle_6 { position: absolute; overflow: visible; width: 410.5px; height: 210px; left: 0px; top: 34px; } #Rectangle_7 { opacity: 0.05; fill: rgba(255,255,255,1); } .Rectangle_7 { position: absolute; overflow: visible; width: 410.5px; height: 210px; left: 410px; top: 34px; } #Rectangle_8 { opacity: 0.09; fill: rgba(255,255,255,1); } .Rectangle_8 { position: absolute; overflow: visible; width: 410.5px; height: 210px; left: 820px; top: 34px; } #hanandito-adi-kieIGjdPTno-unsp { position: absolute; width: 554px; height: 238px; left: 1229px; top: 0px; overflow: visible; } #Footer_1 { position: absolute; width: 366px; height: 151px; left: 203px; top: 892px; overflow: visible; } #n_1_z { left: 0px; top: 0px; position: absolute; overflow: visible; width: 33px; height: 30px; text-align: left; font-family: Open Sans; font-style: normal; font-weight: bold; font-size: 22px; color: rgba(255,255,255,1); } #Dieng_Plateau { left: 0px; top: 30px; position: absolute; overflow: visible; width: 280px; height: 30px; text-align: left; font-family: Open Sans; font-style: normal; font-weight: bold; font-size: 22px; color: rgba(255,255,255,1); } #Moutain_views_jungles_and_dist { left: 0px; top: 91px; position: absolute; overflow: visible; width: 367px; height: 60px; text-align: left; font-family: Open Sans; font-style: normal; font-weight: bold; font-size: 22px; color: rgba(255,255,255,1); } #Footer_2 { position: absolute; width: 264px; height: 151px; left: 553.966px; top: 892px; overflow: visible; } #One_of_the_most_beautiful_neib { left: 0px; top: 97px; position: absolute; overflow: visible; width: 265px; height: 54px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: normal; font-size: 20px; color: rgba(255,255,255,1); } #n_2 { left: 0px; top: 0px; position: absolute; overflow: visible; width: 35px; height: 35px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 26px; color: rgba(255,255,255,1); } #Ubud { left: 0px; top: 53px; position: absolute; overflow: visible; width: 144px; height: 35px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 26px; color: rgba(255,255,255,1); } #Footer_3 { position: absolute; width: 264px; height: 152px; left: 910px; top: 886px; overflow: visible; } #This_volcano_has_become_one_of { left: 0px; top: 98px; position: absolute; overflow: visible; width: 265px; height: 54px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: normal; font-size: 20px; color: rgba(255,255,255,1); } #n_3 { left: 0px; top: 0px; position: absolute; overflow: visible; width: 35px; height: 35px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 26px; color: rgba(255,255,255,1); } #Mount_Bromo { left: 0px; top: 53px; position: absolute; overflow: visible; width: 179px; height: 54px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 26px; color: rgba(255,255,255,1); } #Social { position: absolute; width: 34.842px; height: 239.66px; left: 55.085px; top: 818.301px; overflow: visible; } #In { transform: translate(-55.085px, -818.301px) matrix(1,0,0,1,55.5057,916.6307) rotate(265deg); transform-origin: center; left: 0px; top: 0px; position: absolute; overflow: visible; width: 35px; height: 32px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 24px; color: rgba(255,255,255,1); } #Tw { transform: translate(-55.085px, -818.301px) matrix(1,0,0,1,55.5057,820.6307) rotate(265deg); transform-origin: center; left: 0px; top: 0px; position: absolute; overflow: visible; width: 35px; height: 32px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 24px; color: rgba(255,255,255,1); } #Fb { transform: translate(-55.085px, -818.301px) matrix(1,0,0,1,55.5057,1023.6306) rotate(265deg); transform-origin: center; left: 0px; top: 0px; position: absolute; overflow: visible; width: 35px; height: 32px; text-align: left; font-family: Segoe UI; font-style: normal; font-weight: bold; font-size: 24px; color: rgba(255,255,255,1); } #Play { position: absolute; width: 72px; height: 72px; left: 1603px; top: 932px; overflow: visible; } #Ellipse_1 { fill: rgba(255,255,255,1); stroke: rgba(112,112,112,1); stroke-width: 1px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Ellipse_1 { position: absolute; overflow: visible; width: 72px; height: 72px; left: 0px; top: 0px; } #Icon_awesome-play { fill: rgba(0,0,0,1); } .Icon_awesome-play { overflow: visible; position: absolute; width: 31.499px; height: 36.001px; left: 24.35px; top: 17.999px; transform: matrix(1,0,0,1,0,0); } #Navigation { position: absolute; width: 1619px; height: 34px; left: 90px; top: 124px; overflow: visible; } #Destinations { left: 234px; top: 0px; position: absolute; overflow: visible; width: 182px; height: 34px; text-align: left; font-family: Bebas Kai; font-style: normal; font-weight: normal; font-size: 28px; color: rgba(255,255,255,1); text-transform: uppercase; } #Experiences { left: 441px; top: 0px; position: absolute; overflow: visible; width: 182px; height: 34px; text-align: left; font-family: Bebas Kai; font-style: normal; font-weight: normal; font-size: 28px; color: rgba(255,255,255,1); text-transform: uppercase; } #About { left: 673px; top: 0px; position: absolute; overflow: visible; width: 182px; height: 34px; text-align: left; font-family: Bebas Kai; font-style: normal; font-weight: normal; font-size: 28px; color: rgba(255,255,255,1); text-transform: uppercase; } #Gallery { left: 905px; top: 0px; position: absolute; overflow: visible; width: 182px; height: 34px; text-align: left; font-family: Bebas Kai; font-style: normal; font-weight: normal; font-size: 28px; color: rgba(255,255,255,1); text-transform: uppercase; } #Rectangle_1 { fill: rgba(255,255,255,1); stroke: rgba(112,112,112,1); stroke-width: 1px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Rectangle_1 { position: absolute; overflow: visible; width: 24px; height: 24px; left: 0px; top: 0px; } #Rectangle_2 { fill: rgba(255,255,255,1); stroke: rgba(112,112,112,1); stroke-width: 1px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Rectangle_2 { position: absolute; overflow: visible; width: 24px; height: 24px; left: 1595px; top: 0px; } #Rectangle_3 { fill: rgba(255,255,255,1); stroke: rgba(112,112,112,1); stroke-width: 1px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Rectangle_3 { position: absolute; overflow: visible; width: 24px; height: 24px; left: 1504px; top: 0px; } #Icon_awesome-search { fill: rgba(0,0,0,1); stroke: rgba(4,4,4,1); stroke-width: 1px; stroke-linejoin: miter; stroke-linecap: butt; stroke-miterlimit: 4; shape-rendering: auto; } .Icon_awesome-search { overflow: visible; position: absolute; width: 21px; height: 20.081px; left: 1506px; top: 4.458px; transform: matrix(1,0,0,1,0,0); } #Icon_awesome-map-marker-alt { fill: rgba(0,0,0,1); } .Icon_awesome-map-marker-alt { overflow: visible; position: absolute; width: 20px; height: 19px; left: 2.483px; top: 3.458px; transform: matrix(1,0,0,1,0,0); } #Icon_feather-grid { position: absolute; width: 20px; height: 19px; left: 1594.097px; top: 0.96px; overflow: visible; } #Path_1 { fill: rgba(179,138,197,1); stroke: rgba(0,0,0,1); stroke-width: 3px; stroke-linejoin: round; stroke-linecap: round; stroke-miterlimit: 4; shape-rendering: auto; } .Path_1 { overflow: visible; position: absolute; width: 13.5px; height: 13.5px; left: 0px; top: 0px; transform: matrix(1,0,0,1,0,0); } #Path_2 { fill: transparent; stroke: rgba(0,0,0,1); stroke-width: 3px; stroke-linejoin: round; stroke-linecap: round; stroke-miterlimit: 4; shape-rendering: auto; } .Path_2 { overflow: visible; position: absolute; width: 13.5px; height: 13.5px; left: 9.5px; top: 0px; transform: matrix(1,0,0,1,0,0); } #Path_3 { fill: transparent; stroke: rgba(0,0,0,1); stroke-width: 3px; stroke-linejoin: round; stroke-linecap: round; stroke-miterlimit: 4; shape-rendering: auto; } .Path_3 { overflow: visible; position: absolute; width: 13.5px; height: 13.5px; left: 9.5px; top: 8.5px; transform: matrix(1,0,0,1,0,0); } #Path_4 { fill: transparent; stroke: rgba(0,0,0,1); stroke-width: 3px; stroke-linejoin: round; stroke-linecap: round; stroke-miterlimit: 4; shape-rendering: auto; } .Path_4 { overflow: visible; position: absolute; width: 13.5px; height: 13.5px; left: 0px; top: 8.5px; transform: matrix(1,0,0,1,0,0); } </style> <script id="applicationScript"> /////////////////////////////////////// // INITIALIZATION /////////////////////////////////////// /** * Functionality for scaling, showing by media query, and navigation between multiple pages on a single page. * Code subject to change. **/ if (window.console==null) { window["console"] = { log : function() {} } }; // some browsers do not set console var Application = function() { // event constants this.prefix = "--web-"; this.NAVIGATION_CHANGE = "viewChange"; this.VIEW_NOT_FOUND = "viewNotFound"; this.VIEW_CHANGE = "viewChange"; this.VIEW_CHANGING = "viewChanging"; this.STATE_NOT_FOUND = "stateNotFound"; this.APPLICATION_COMPLETE = "applicationComplete"; this.APPLICATION_RESIZE = "applicationResize"; this.SIZE_STATE_NAME = "data-is-view-scaled"; this.STATE_NAME = this.prefix + "state"; this.lastTrigger = null; this.lastView = null; this.lastState = null; this.lastOverlay = null; this.currentView = null; this.currentState = null; this.currentOverlay = null; this.currentQuery = {index: 0, rule: null, mediaText: null, id: null}; this.inclusionQuery = "(min-width: 0px)"; this.exclusionQuery = "none and (min-width: 99999px)"; this.LastModifiedDateLabelName = "LastModifiedDateLabel"; this.viewScaleSliderId = "ViewScaleSliderInput"; this.pageRefreshedName = "showPageRefreshedNotification"; this.application = null; this.applicationStylesheet = null; this.showByMediaQuery = null; this.mediaQueryDictionary = {}; this.viewsDictionary = {}; this.addedViews = []; this.viewStates = []; this.views = []; this.viewIds = []; this.viewQueries = {}; this.overlays = {}; this.overlayIds = []; this.numberOfViews = 0; this.verticalPadding = 0; this.horizontalPadding = 0; this.stateName = null; this.viewScale = 1; this.viewLeft = 0; this.viewTop = 0; this.horizontalScrollbarsNeeded = false; this.verticalScrollbarsNeeded = false; // view settings this.showUpdateNotification = false; this.showNavigationControls = false; this.scaleViewsToFit = false; this.scaleToFitOnDoubleClick = false; this.actualSizeOnDoubleClick = false; this.scaleViewsOnResize = false; this.navigationOnKeypress = false; this.showViewName = false; this.enableDeepLinking = true; this.refreshPageForChanges = false; this.showRefreshNotifications = true; // view controls this.scaleViewSlider = null; this.lastModifiedLabel = null; this.supportsPopState = false; // window.history.pushState!=null; this.initialized = false; // refresh properties this.refreshDuration = 250; this.lastModifiedDate = null; this.refreshRequest = null; this.refreshInterval = null; this.refreshContent = null; this.refreshContentSize = null; this.refreshCheckContent = false; this.refreshCheckContentSize = false; var self = this; self.initialize = function(event) { var view = self.getVisibleView(); var views = self.getVisibleViews(); if (view==null) view = self.getInitialView(); self.collectViews(); self.collectOverlays(); self.collectMediaQueries(); for (let index = 0; index < views.length; index++) { var view = views[index]; self.setViewOptions(view); self.setViewVariables(view); self.centerView(view); } // sometimes the body size is 0 so we call this now and again later if (self.initialized) { window.addEventListener(self.NAVIGATION_CHANGE, self.viewChangeHandler); window.addEventListener("keyup", self.keypressHandler); window.addEventListener("keypress", self.keypressHandler); window.addEventListener("resize", self.resizeHandler); window.document.addEventListener("dblclick", self.doubleClickHandler); if (self.supportsPopState) { window.addEventListener('popstate', self.popStateHandler); } else { window.addEventListener('hashchange', self.hashChangeHandler); } // we are ready to go window.dispatchEvent(new Event(self.APPLICATION_COMPLETE)); } if (self.initialized==false) { if (self.enableDeepLinking) { self.syncronizeViewToURL(); } if (self.refreshPageForChanges) { self.setupRefreshForChanges(); } self.initialized = true; } if (self.scaleViewsToFit) { self.viewScale = self.scaleViewToFit(view); if (self.viewScale<0) { setTimeout(self.scaleViewToFit, 500, view); } } else if (view) { self.viewScale = self.getViewScaleValue(view); self.centerView(view); self.updateSliderValue(self.viewScale); } else { // no view found } if (self.showUpdateNotification) { self.showNotification(); } //"addEventListener" in window ? null : window.addEventListener = window.attachEvent; //"addEventListener" in document ? null : document.addEventListener = document.attachEvent; } /////////////////////////////////////// // AUTO REFRESH /////////////////////////////////////// self.setupRefreshForChanges = function() { self.refreshRequest = new XMLHttpRequest(); if (!self.refreshRequest) { return false; } // get document start values immediately self.requestRefreshUpdate(); } /** * Attempt to check the last modified date by the headers * or the last modified property from the byte array (experimental) **/ self.requestRefreshUpdate = function() { var url = document.location.href; var protocol = window.location.protocol; var method; try { if (self.refreshCheckContentSize) { self.refreshRequest.open('HEAD', url, true); } else if (self.refreshCheckContent) { self.refreshContent = document.documentElement.outerHTML; self.refreshRequest.open('GET', url, true); self.refreshRequest.responseType = "text"; } else { // get page last modified date for the first call to compare to later if (self.lastModifiedDate==null) { // File system does not send headers in FF so get blob if possible if (protocol=="file:") { self.refreshRequest.open("GET", url, true); self.refreshRequest.responseType = "blob"; } else { self.refreshRequest.open("HEAD", url, true); self.refreshRequest.responseType = "blob"; } self.refreshRequest.onload = self.refreshOnLoadOnceHandler; // In some browsers (Chrome & Safari) this error occurs at send: // // Chrome - Access to XMLHttpRequest at 'file:///index.html' from origin 'null' // has been blocked by CORS policy: // Cross origin requests are only supported for protocol schemes: // http, data, chrome, chrome-extension, https. // // Safari - XMLHttpRequest cannot load file:///Users/user/Public/index.html. Cross origin requests are only supported for HTTP. // // Solution is to run a local server, set local permissions or test in another browser self.refreshRequest.send(null); // In MS browsers the following behavior occurs possibly due to an AJAX call to check last modified date: // // DOM7011: The code on this page disabled back and forward caching. // In Brave (Chrome) error when on the server // index.js:221 HEAD https://www.example.com/ net::ERR_INSUFFICIENT_RESOURCES // self.refreshRequest.send(null); } else { self.refreshRequest = new XMLHttpRequest(); self.refreshRequest.onreadystatechange = self.refreshHandler; self.refreshRequest.ontimeout = function() { self.log("Couldn't find page to check for updates"); } var method; if (protocol=="file:") { method = "GET"; } else { method = "HEAD"; } //refreshRequest.open('HEAD', url, true); self.refreshRequest.open(method, url, true); self.refreshRequest.responseType = "blob"; self.refreshRequest.send(null); } } } catch (error) { self.log("Refresh failed for the following reason:") self.log(error); } } self.refreshHandler = function() { var contentSize; try { if (self.refreshRequest.readyState === XMLHttpRequest.DONE) { if (self.refreshRequest.status === 2 || self.refreshRequest.status === 200) { var pageChanged = false; self.updateLastModifiedLabel(); if (self.refreshCheckContentSize) { var lastModifiedHeader = self.refreshRequest.getResponseHeader("Last-Modified"); contentSize = self.refreshRequest.getResponseHeader("Content-Length"); //lastModifiedDate = refreshRequest.getResponseHeader("Last-Modified"); var headers = self.refreshRequest.getAllResponseHeaders(); var hasContentHeader = headers.indexOf("Content-Length")!=-1; if (hasContentHeader) { contentSize = self.refreshRequest.getResponseHeader("Content-Length"); // size has not been set yet if (self.refreshContentSize==null) { self.refreshContentSize = contentSize; // exit and let interval call this method again return; } if (contentSize!=self.refreshContentSize) { pageChanged = true; } } } else if (self.refreshCheckContent) { if (self.refreshRequest.responseText!=self.refreshContent) { pageChanged = true; } } else { lastModifiedHeader = self.getLastModified(self.refreshRequest); if (self.lastModifiedDate!=lastModifiedHeader) { self.log("lastModifiedDate:" + self.lastModifiedDate + ",lastModifiedHeader:" +lastModifiedHeader); pageChanged = true; } } if (pageChanged) { clearInterval(self.refreshInterval); self.refreshUpdatedPage(); return; } } else { self.log('There was a problem with the request.'); } } } catch( error ) { //console.log('Caught Exception: ' + error); } } self.refreshOnLoadOnceHandler = function(event) { // get the last modified date if (self.refreshRequest.response) { self.lastModifiedDate = self.getLastModified(self.refreshRequest); if (self.lastModifiedDate!=null) { if (self.refreshInterval==null) { self.refreshInterval = setInterval(self.requestRefreshUpdate, self.refreshDuration); } } else { self.log("Could not get last modified date from the server"); } } } self.refreshUpdatedPage = function() { if (self.showRefreshNotifications) { var date = new Date().setTime((new Date().getTime()+10000)); document.cookie = encodeURIComponent(self.pageRefreshedName) + "=true" + "; max-age=6000;" + " path=/"; } document.location.reload(true); } self.showNotification = function(duration) { var notificationID = self.pageRefreshedName+"ID"; var notification = document.getElementById(notificationID); if (duration==null) duration = 4000; if (notification!=null) {return;} notification = document.createElement("div"); notification.id = notificationID; notification.textContent = "PAGE UPDATED"; var styleRule = "" styleRule = "position: fixed; padding: 7px 16px 6px 16px; font-family: Arial, sans-serif; font-size: 10px; font-weight: bold; left: 50%;"; styleRule += "top: 20px; background-color: rgba(0,0,0,.5); border-radius: 12px; color:rgb(235, 235, 235); transition: all 2s linear;"; styleRule += "transform: translateX(-50%); letter-spacing: .5px; filter: drop-shadow(2px 2px 6px rgba(0, 0, 0, .1)); cursor: pointer"; notification.setAttribute("style", styleRule); notification.className = "PageRefreshedClass"; notification.addEventListener("click", function() { notification.parentNode.removeChild(notification); }); document.body.appendChild(notification); setTimeout(function() { notification.style.opacity = "0"; notification.style.filter = "drop-shadow( 0px 0px 0px rgba(0,0,0, .5))"; setTimeout(function() { try { notification.parentNode.removeChild(notification); } catch(error) {} }, duration) }, duration); document.cookie = encodeURIComponent(self.pageRefreshedName) + "=; max-age=1; path=/"; } /** * Get the last modified date from the header * or file object after request has been received **/ self.getLastModified = function(request) { var date; // file protocol - FILE object with last modified property if (request.response && request.response.lastModified) { date = request.response.lastModified; } // http protocol - check headers if (date==null) { date = request.getResponseHeader("Last-Modified"); } return date; } self.updateLastModifiedLabel = function() { var labelValue = ""; if (self.lastModifiedLabel==null) { self.lastModifiedLabel = document.getElementById("LastModifiedLabel"); } if (self.lastModifiedLabel) { var seconds = parseInt(((new Date().getTime() - Date.parse(document.lastModified)) / 1000 / 60) * 100 + ""); var minutes = 0; var hours = 0; if (seconds < 60) { seconds = Math.floor(seconds/10)*10; labelValue = seconds + " seconds"; } else { minutes = parseInt((seconds/60) + ""); if (minutes>60) { hours = parseInt((seconds/60/60) +""); labelValue += hours==1 ? " hour" : " hours"; } else { labelValue = minutes+""; labelValue += minutes==1 ? " minute" : " minutes"; } } if (seconds<10) { labelValue = "Updated now"; } else { labelValue = "Updated " + labelValue + " ago"; } if (self.lastModifiedLabel.firstElementChild) { self.lastModifiedLabel.firstElementChild.textContent = labelValue; } else if ("textContent" in self.lastModifiedLabel) { self.lastModifiedLabel.textContent = labelValue; } } } self.getShortString = function(string, length) { if (length==null) length = 30; string = string!=null ? string.substr(0, length).replace(/n/g, "") : "[String is null]"; return string; } self.getShortNumber = function(value, places) { if (places==null || places<1) places = 4; value = Math.round(value * Math.pow(10,places)) / Math.pow(10, places); return value; } /////////////////////////////////////// // NAVIGATION CONTROLS /////////////////////////////////////// self.updateViewLabel = function() { var viewNavigationLabel = document.getElementById("ViewNavigationLabel"); var view = self.getVisibleView(); var viewIndex = view ? self.getViewIndex(view) : -1; var viewName = view ? self.getViewPreferenceValue(view, self.prefix + "view-name") : null; var viewId = view ? view.id : null; if (viewNavigationLabel && view) { if (viewName && viewName.indexOf('"')!=-1) { viewName = viewName.replace(/"/g, ""); } if (self.showViewName) { viewNavigationLabel.textContent = viewName; self.setTooltip(viewNavigationLabel, viewIndex + 1 + " of " + self.numberOfViews); } else { viewNavigationLabel.textContent = viewIndex + 1 + " of " + self.numberOfViews; self.setTooltip(viewNavigationLabel, viewName); } } } self.updateURL = function(view) { view = view == null ? self.getVisibleView() : view; var viewId = view ? view.id : null var viewFragment = view ? "#"+ viewId : null; if (viewId && self.viewIds.length>1 && self.enableDeepLinking) { if (self.supportsPopState==false) { self.setFragment(viewId); } else { if (viewFragment!=window.location.hash) { if (window.location.hash==null) { window.history.replaceState({name:viewId}, null, viewFragment); } else { window.history.pushState({name:viewId}, null, viewFragment); } } } } } self.updateURLState = function(view, stateName) { stateName = view && (stateName=="" || stateName==null) ? self.getStateNameByViewId(view.id) : stateName; if (self.supportsPopState==false) { self.setFragment(stateName); } else { if (stateName!=window.location.hash) { if (window.location.hash==null) { window.history.replaceState({name:view.viewId}, null, stateName); } else { window.history.pushState({name:view.viewId}, null, stateName); } } } } self.setFragment = function(value) { window.location.hash = "#" + value; } self.setTooltip = function(element, value) { // setting the tooltip in edge causes a page crash on hover if (/Edge/.test(navigator.userAgent)) { return; } if ("title" in element) { element.title = value; } } self.getStylesheetRules = function(styleSheet) { try { if (styleSheet) return styleSheet.cssRules || styleSheet.rules; return document.styleSheets[0]["cssRules"] || document.styleSheets[0]["rules"]; } catch (error) { // ERRORS: // SecurityError: The operation is insecure. // Errors happen when script loads before stylesheet or loading an external css locally // InvalidAccessError: A parameter or an operation is not supported by the underlying object // Place script after stylesheet console.log(error); if (error.toString().indexOf("The operation is insecure")!=-1) { console.log("Load the stylesheet before the script or load the stylesheet inline until it can be loaded on a server") } return []; } } /** * If single page application hide all of the views. * @param {Number} selectedIndex if provided shows the view at index provided **/ self.hideViews = function(selectedIndex, animation) { var rules = self.getStylesheetRules(); var queryIndex = 0; var numberOfRules = rules!=null ? rules.length : 0; // loop through rules and hide media queries except selected for (var i=0;i<numberOfRules;i++) { var rule = rules[i]; var cssText = rule && rule.cssText; if (rule.media!=null && cssText.match("--web-view-name:")) { if (queryIndex==selectedIndex) { self.currentQuery.mediaText = rule.conditionText; self.currentQuery.index = selectedIndex; self.currentQuery.rule = rule; self.enableMediaQuery(rule); } else { if (animation) { self.fadeOut(rule) } else { self.disableMediaQuery(rule); } } queryIndex++; } } self.numberOfViews = queryIndex; self.updateViewLabel(); self.updateURL(); self.dispatchViewChange(); var view = self.getVisibleView(); var viewIndex = view ? self.getViewIndex(view) : -1; return viewIndex==selectedIndex ? view : null; } /** * If single page application hide all of the views. * @param {HTMLElement} selectedView if provided shows the view passed in **/ self.hideAllViews = function(selectedView, animation) { var views = self.views; var queryIndex = 0; var numberOfViews = views!=null ? views.length : 0; // loop through rules and hide media queries except selected for (var i=0;i<numberOfViews;i++) { var viewData = views[i]; var view = viewData && viewData.view; var mediaRule = viewData && viewData.mediaRule; if (view==selectedView) { self.currentQuery.mediaText = mediaRule.conditionText; self.currentQuery.index = queryIndex; self.currentQuery.rule = mediaRule; self.enableMediaQuery(mediaRule); } else { if (animation) { self.fadeOut(mediaRule) } else { self.disableMediaQuery(mediaRule); } } queryIndex++; } self.numberOfViews = queryIndex; self.updateViewLabel(); self.updateURL(); self.dispatchViewChange(); var visibleView = self.getVisibleView(); return visibleView==selectedView ? selectedView : null; } /** * Hide view * @param {Object} view element to hide **/ self.hideView = function(view) { var rule = view ? self.mediaQueryDictionary[view.id] : null; if (rule) { self.disableMediaQuery(rule); } } /** * Hide overlay * @param {Object} overlay element to hide **/ self.hideOverlay = function(overlay) { var rule = overlay ? self.mediaQueryDictionary[overlay.id] : null; if (rule) { self.disableMediaQuery(rule); //if (self.showByMediaQuery) { overlay.style.display = "none"; //} } } /** * Show the view by media query. Does not hide current views * Sets view options by default * @param {Object} view element to show * @param {Boolean} setViewOptions sets view options if null or true */ self.showViewByMediaQuery = function(view, setViewOptions) { var id = view ? view.id : null; var query = id ? self.mediaQueryDictionary[id] : null; var isOverlay = view ? self.isOverlay(view) : false; setViewOptions = setViewOptions==null ? true : setViewOptions; if (query) { self.enableMediaQuery(query); if (isOverlay && view && setViewOptions) { self.setViewVariables(null, view); } else { if (view && setViewOptions) self.setViewOptions(view); if (view && setViewOptions) self.setViewVariables(view); } } } /** * Show the view. Does not hide current views */ self.showView = function(view, setViewOptions) { var id = view ? view.id : null; var query = id ? self.mediaQueryDictionary[id] : null; var display = null; setViewOptions = setViewOptions==null ? true : setViewOptions; if (query) { self.enableMediaQuery(query); if (view==null) view =self.getVisibleView(); if (view && setViewOptions) self.setViewOptions(view); } else if (id) { display = window.getComputedStyle(view).getPropertyValue("display"); if (display=="" || display=="none") { view.style.display = "block"; } } if (view) { if (self.currentView!=null) { self.lastView = self.currentView; } self.currentView = view; } } self.showViewById = function(id, setViewOptions) { var view = id ? self.getViewById(id) : null; if (view) { self.showView(view); return; } self.log("View not found '" + id + "'"); } self.getElementView = function(element) { var view = element; var viewFound = false; while (viewFound==false || view==null) { if (view && self.viewsDictionary[view.id]) { return view; } view = view.parentNode; } } /** * Show overlay over view * @param {Event | HTMLElement} event event or html element with styles applied * @param {String} id id of view or view reference * @param {Number} x x location * @param {Number} y y location */ self.showOverlay = function(event, id, x, y) { var overlay = id && typeof id === 'string' ? self.getViewById(id) : id ? id : null; var query = overlay ? self.mediaQueryDictionary[overlay.id] : null; var centerHorizontally = false; var centerVertically = false; var anchorLeft = false; var anchorTop = false; var anchorRight = false; var anchorBottom = false; var display = null; var reparent = true; var view = null; if (overlay==null || overlay==false) { self.log("Overlay not found, '"+ id + "'"); return; } // get enter animation - event target must have css variables declared if (event) { var button = event.currentTarget || event; // can be event or htmlelement var buttonComputedStyles = getComputedStyle(button); var actionTargetValue = buttonComputedStyles.getPropertyValue(self.prefix+"action-target").trim(); var animation = buttonComputedStyles.getPropertyValue(self.prefix+"animation").trim(); var isAnimated = animation!=""; var targetType = buttonComputedStyles.getPropertyValue(self.prefix+"action-type").trim(); var actionTarget = self.application ? null : self.getElement(actionTargetValue); var actionTargetStyles = actionTarget ? actionTarget.style : null; if (actionTargetStyles) { actionTargetStyles.setProperty("animation", animation); } if ("stopImmediatePropagation" in event) { event.stopImmediatePropagation(); } } if (self.application==false || targetType=="page") { document.location.href = "./" + actionTargetValue; return; } // remove any current overlays if (self.currentOverlay) { // act as switch if same button if (self.currentOverlay==actionTarget || self.currentOverlay==null) { if (self.lastTrigger==button) { self.removeOverlay(isAnimated); return; } } else { self.removeOverlay(isAnimated); } } if (reparent) { view = self.getElementView(button); if (view) { view.appendChild(overlay); } } if (query) { //self.setElementAnimation(overlay, null); //overlay.style.animation = animation; self.enableMediaQuery(query); var display = overlay && overlay.style.display; if (overlay && display=="" || display=="none") { overlay.style.display = "block"; //self.setViewOptions(overlay); } // add animation defined in event target style declaration if (animation && self.supportAnimations) { self.fadeIn(overlay, false, animation); } } else if (id) { display = window.getComputedStyle(overlay).getPropertyValue("display"); if (display=="" || display=="none") { overlay.style.display = "block"; } // add animation defined in event target style declaration if (animation && self.supportAnimations) { self.fadeIn(overlay, false, animation); } } // do not set x or y position if centering var horizontal = self.prefix + "center-horizontally"; var vertical = self.prefix + "center-vertically"; var style = overlay.style; var transform = []; centerHorizontally = self.getIsStyleDefined(id, horizontal) ? self.getViewPreferenceBoolean(overlay, horizontal) : false; centerVertically = self.getIsStyleDefined(id, vertical) ? self.getViewPreferenceBoolean(overlay, vertical) : false; anchorLeft = self.getIsStyleDefined(id, "left"); anchorRight = self.getIsStyleDefined(id, "right"); anchorTop = self.getIsStyleDefined(id, "top"); anchorBottom = self.getIsStyleDefined(id, "bottom"); if (self.viewsDictionary[overlay.id] && self.viewsDictionary[overlay.id].styleDeclaration) { style = self.viewsDictionary[overlay.id].styleDeclaration.style; } if (centerHorizontally) { style.left = "50%"; style.transformOrigin = "0 0"; transform.push("translateX(-50%)"); } else if (anchorRight && anchorLeft) { style.left = x + "px"; } else if (anchorRight) { //style.right = x + "px"; } else { style.left = x + "px"; } if (centerVertically) { style.top = "50%"; transform.push("translateY(-50%)"); style.transformOrigin = "0 0"; } else if (anchorTop && anchorBottom) { style.top = y + "px"; } else if (anchorBottom) { //style.bottom = y + "px"; } else { style.top = y + "px"; } if (transform.length) { style.transform = transform.join(" "); } self.currentOverlay = overlay; self.lastTrigger = button; } self.goBack = function() { if (self.currentOverlay) { self.removeOverlay(); } else if (self.lastView) { self.goToView(self.lastView.id); } } self.removeOverlay = function(animate) { var overlay = self.currentOverlay; animate = animate===false ? false : true; if (overlay) { var style = overlay.style; if (style.animation && self.supportAnimations && animate) { self.reverseAnimation(overlay, true); var duration = self.getAnimationDuration(style.animation, true); setTimeout(function() { self.setElementAnimation(overlay, null); self.hideOverlay(overlay); self.currentOverlay = null; }, duration); } else { self.setElementAnimation(overlay, null); self.hideOverlay(overlay); self.currentOverlay = null; } } } /** * Reverse the animation and hide after * @param {Object} target element with animation * @param {Boolean} hide hide after animation ends */ self.reverseAnimation = function(target, hide) { var lastAnimation = null; var style = target.style; style.animationPlayState = "paused"; lastAnimation = style.animation; style.animation = null; style.animationPlayState = "paused"; if (hide) { //target.addEventListener("animationend", self.animationEndHideHandler); var duration = self.getAnimationDuration(lastAnimation, true); var isOverlay = self.isOverlay(target); setTimeout(function() { self.setElementAnimation(target, null); if (isOverlay) { self.hideOverlay(target); } else { self.hideView(target); } }, duration); } setTimeout(function() { style.animation = lastAnimation; style.animationPlayState = "paused"; style.animationDirection = "reverse"; style.animationPlayState = "running"; }, 30); } self.animationEndHandler = function(event) { var target = event.currentTarget; self.dispatchEvent(new Event(event.type)); } self.isOverlay = function(view) { var result = view ? self.getViewPreferenceBoolean(view, self.prefix + "is-overlay") : false; return result; } self.animationEndHideHandler = function(event) { var target = event.currentTarget; self.setViewVariables(null, target); self.hideView(target); target.removeEventListener("animationend", self.animationEndHideHandler); } self.animationEndShowHandler = function(event) { var target = event.currentTarget; target.removeEventListener("animationend", self.animationEndShowHandler); } self.setViewOptions = function(view) { if (view) { self.minimumScale = self.getViewPreferenceValue(view, self.prefix + "minimum-scale"); self.maximumScale = self.getViewPreferenceValue(view, self.prefix + "maximum-scale"); self.scaleViewsToFit = self.getViewPreferenceBoolean(view, self.prefix + "scale-to-fit"); self.scaleToFitType = self.getViewPreferenceValue(view, self.prefix + "scale-to-fit-type"); self.scaleToFitOnDoubleClick = self.getViewPreferenceBoolean(view, self.prefix + "scale-on-double-click"); self.actualSizeOnDoubleClick = self.getViewPreferenceBoolean(view, self.prefix + "actual-size-on-double-click"); self.scaleViewsOnResize = self.getViewPreferenceBoolean(view, self.prefix + "scale-on-resize"); self.enableScaleUp = self.getViewPreferenceBoolean(view, self.prefix + "enable-scale-up"); self.centerHorizontally = self.getViewPreferenceBoolean(view, self.prefix + "center-horizontally"); self.centerVertically = self.getViewPreferenceBoolean(view, self.prefix + "center-vertically"); self.navigationOnKeypress = self.getViewPreferenceBoolean(view, self.prefix + "navigate-on-keypress"); self.showViewName = self.getViewPreferenceBoolean(view, self.prefix + "show-view-name"); self.refreshPageForChanges = self.getViewPreferenceBoolean(view, self.prefix + "refresh-for-changes"); self.refreshPageForChangesInterval = self.getViewPreferenceValue(view, self.prefix + "refresh-interval"); self.showNavigationControls = self.getViewPreferenceBoolean(view, self.prefix + "show-navigation-controls"); self.scaleViewSlider = self.getViewPreferenceBoolean(view, self.prefix + "show-scale-controls"); self.enableDeepLinking = self.getViewPreferenceBoolean(view, self.prefix + "enable-deep-linking"); self.singlePageApplication = self.getViewPreferenceBoolean(view, self.prefix + "application"); self.showByMediaQuery = self.getViewPreferenceBoolean(view, self.prefix + "show-by-media-query"); self.showUpdateNotification = document.cookie!="" ? document.cookie.indexOf(self.pageRefreshedName)!=-1 : false; self.imageComparisonDuration = self.getViewPreferenceValue(view, self.prefix + "image-comparison-duration"); self.supportAnimations = self.getViewPreferenceBoolean(view, self.prefix + "enable-animations", true); if (self.scaleViewsToFit) { var newScaleValue = self.scaleViewToFit(view); if (newScaleValue<0) { setTimeout(self.scaleViewToFit, 500, view); } } else { self.viewScale = self.getViewScaleValue(view); self.viewToFitWidthScale = self.getViewFitToViewportWidthScale(view, self.enableScaleUp) self.viewToFitHeightScale = self.getViewFitToViewportScale(view, self.enableScaleUp); self.updateSliderValue(self.viewScale); } if (self.imageComparisonDuration!=null) { // todo } if (self.refreshPageForChangesInterval!=null) { self.refreshDuration = Number(self.refreshPageForChangesInterval); } } } self.previousView = function(event) { var rules = self.getStylesheetRules(); var view = self.getVisibleView() var index = view ? self.getViewIndex(view) : -1; var prevQueryIndex = index!=-1 ? index-1 : self.currentQuery.index-1; var queryIndex = 0; var numberOfRules = rules!=null ? rules.length : 0; if (event) { event.stopImmediatePropagation(); } if (prevQueryIndex<0) { return; } // loop through rules and hide media queries except selected for (var i=0;i<numberOfRules;i++) { var rule = rules[i]; if (rule.media!=null) { if (queryIndex==prevQueryIndex) { self.currentQuery.mediaText = rule.conditionText; self.currentQuery.index = prevQueryIndex; self.currentQuery.rule = rule; self.enableMediaQuery(rule); self.updateViewLabel(); self.updateURL(); self.dispatchViewChange(); } else { self.disableMediaQuery(rule); } queryIndex++; } } } self.nextView = function(event) { var rules = self.getStylesheetRules(); var view = self.getVisibleView(); var index = view ? self.getViewIndex(view) : -1; var nextQueryIndex = index!=-1 ? index+1 : self.currentQuery.index+1; var queryIndex = 0; var numberOfRules = rules!=null ? rules.length : 0; var numberOfMediaQueries = self.getNumberOfMediaRules(); if (event) { event.stopImmediatePropagation(); } if (nextQueryIndex>=numberOfMediaQueries) { return; } // loop through rules and hide media queries except selected for (var i=0;i<numberOfRules;i++) { var rule = rules[i]; if (rule.media!=null) { if (queryIndex==nextQueryIndex) { self.currentQuery.mediaText = rule.conditionText; self.currentQuery.index = nextQueryIndex; self.currentQuery.rule = rule; self.enableMediaQuery(rule); self.updateViewLabel(); self.updateURL(); self.dispatchViewChange(); } else { self.disableMediaQuery(rule); } queryIndex++; } } } /** * Enables a view via media query */ self.enableMediaQuery = function(rule) { try { rule.media.mediaText = self.inclusionQuery; } catch(error) { //self.log(error); rule.conditionText = self.inclusionQuery; } } self.disableMediaQuery = function(rule) { try { rule.media.mediaText = self.exclusionQuery; } catch(error) { rule.conditionText = self.exclusionQuery; } } self.dispatchViewChange = function() { try { var event = new Event(self.NAVIGATION_CHANGE); window.dispatchEvent(event); } catch (error) { // In IE 11: Object doesn't support this action } } self.getNumberOfMediaRules = function() { var rules = self.getStylesheetRules(); var numberOfRules = rules ? rules.length : 0; var numberOfQueries = 0; for (var i=0;i<numberOfRules;i++) { if (rules[i].media!=null) { numberOfQueries++; } } return numberOfQueries; } ///////////////////////////////////////// // VIEW SCALE ///////////////////////////////////////// self.sliderChangeHandler = function(event) { var value = self.getShortNumber(event.currentTarget.value/100); var view = self.getVisibleView(); self.setViewScaleValue(view, false, value, true); } self.updateSliderValue = function(scale) { var slider = document.getElementById(self.viewScaleSliderId); var tooltip = parseInt(scale * 100 + "") + "%"; var inputType; var inputValue; if (slider) { inputValue = self.getShortNumber(scale * 100); if (inputValue!=slider["value"]) { slider["value"] = inputValue; } inputType = slider.getAttributeNS(null, "type"); if (inputType!="range") { // input range is not supported slider.style.display = "none"; } self.setTooltip(slider, tooltip); } } self.viewChangeHandler = function(event) { var view = self.getVisibleView(); var matrix = view ? getComputedStyle(view).transform : null; if (matrix) { self.viewScale = self.getViewScaleValue(view); var scaleNeededToFit = self.getViewFitToViewportScale(view); var isViewLargerThanViewport = scaleNeededToFit<1; // scale large view to fit if scale to fit is enabled if (self.scaleViewsToFit) { self.scaleViewToFit(view); } else { self.updateSliderValue(self.viewScale); } } } self.getViewScaleValue = function(view) { var matrix = getComputedStyle(view).transform; if (matrix) { var matrixArray = matrix.replace("matrix(", "").split(","); var scaleX = parseFloat(matrixArray[0]); var scaleY = parseFloat(matrixArray[3]); var scale = Math.min(scaleX, scaleY); } return scale; } /** * Scales view to scale. * @param {Object} view view to scale. views are in views array * @param {Boolean} scaleToFit set to true to scale to fit. set false to use desired scale value * @param {Number} desiredScale scale to define. not used if scale to fit is false * @param {Boolean} isSliderChange indicates if slider is callee */ self.setViewScaleValue = function(view, scaleToFit, desiredScale, isSliderChange) { var enableScaleUp = self.enableScaleUp; var scaleToFitType = self.scaleToFitType; var minimumScale = self.minimumScale; var maximumScale = self.maximumScale; var hasMinimumScale = !isNaN(minimumScale) && minimumScale!=""; var hasMaximumScale = !isNaN(maximumScale) && maximumScale!=""; var scaleNeededToFit = self.getViewFitToViewportScale(view, enableScaleUp); var scaleNeededToFitWidth = self.getViewFitToViewportWidthScale(view, enableScaleUp); var scaleNeededToFitHeight = self.getViewFitToViewportHeightScale(view, enableScaleUp); var scaleToFitFull = self.getViewFitToViewportScale(view, true); var scaleToFitFullWidth = self.getViewFitToViewportWidthScale(view, true); var scaleToFitFullHeight = self.getViewFitToViewportHeightScale(view, true); var scaleToWidth = scaleToFitType=="width"; var scaleToHeight = scaleToFitType=="height"; var shrunkToFit = false; var topPosition = null; var leftPosition = null; var translateY = null; var translateX = null; var transformValue = ""; var canCenterVertically = true; var canCenterHorizontally = true; var style = view.style; if (view && self.viewsDictionary[view.id] && self.viewsDictionary[view.id].styleDeclaration) { style = self.viewsDictionary[view.id].styleDeclaration.style; } if (scaleToFit && isSliderChange!=true) { if (scaleToFitType=="fit" || scaleToFitType=="") { desiredScale = scaleNeededToFit; } else if (scaleToFitType=="width") { desiredScale = scaleNeededToFitWidth; } else if (scaleToFitType=="height") { desiredScale = scaleNeededToFitHeight; } } else { if (isNaN(desiredScale)) { desiredScale = 1; } } self.updateSliderValue(desiredScale); // scale to fit width if (scaleToWidth && scaleToHeight==false) { canCenterVertically = scaleNeededToFitHeight>=scaleNeededToFitWidth; canCenterHorizontally = scaleNeededToFitWidth>=1 && enableScaleUp==false; if (isSliderChange) { canCenterHorizontally = desiredScale<scaleToFitFullWidth; } else if (scaleToFit) { desiredScale = scaleNeededToFitWidth; } if (hasMinimumScale) { desiredScale = Math.max(desiredScale, Number(minimumScale)); } if (hasMaximumScale) { desiredScale = Math.min(desiredScale, Number(maximumScale)); } desiredScale = self.getShortNumber(desiredScale); canCenterHorizontally = self.canCenterHorizontally(view, "width", enableScaleUp, desiredScale, minimumScale, maximumScale); canCenterVertically = self.canCenterVertically(view, "width", enableScaleUp, desiredScale, minimumScale, maximumScale); if (desiredScale>1 && (enableScaleUp || isSliderChange)) { transformValue = "scale(" + desiredScale + ")"; } else if (desiredScale>=1 && enableScaleUp==false) { transformValue = "scale(" + 1 + ")"; } else { transformValue = "scale(" + desiredScale + ")"; } if (self.centerVertically) { if (canCenterVertically) { translateY = "-50%"; topPosition = "50%"; } else { translateY = "0"; topPosition = "0"; } if (style.top != topPosition) { style.top = topPosition + ""; } if (canCenterVertically) { transformValue += " translateY(" + translateY+ ")"; } } if (self.centerHorizontally) { if (canCenterHorizontally) { translateX = "-50%"; leftPosition = "50%"; } else { translateX = "0"; leftPosition = "0"; } if (style.left != leftPosition) { style.left = leftPosition + ""; } if (canCenterHorizontally) { transformValue += " translateX(" + translateX+ ")"; } } style.transformOrigin = "0 0"; style.transform = transformValue; self.viewScale = desiredScale; self.viewToFitWidthScale = scaleNeededToFitWidth; self.viewToFitHeightScale = scaleNeededToFitHeight; self.viewLeft = leftPosition; self.viewTop = topPosition; return desiredScale; } // scale to fit height if (scaleToHeight && scaleToWidth==false) { //canCenterVertically = scaleNeededToFitHeight>=scaleNeededToFitWidth; //canCenterHorizontally = scaleNeededToFitHeight<=scaleNeededToFitWidth && enableScaleUp==false; canCenterVertically = scaleNeededToFitHeight>=scaleNeededToFitWidth; canCenterHorizontally = scaleNeededToFitWidth>=1 && enableScaleUp==false; if (isSliderChange) { canCenterHorizontally = desiredScale<scaleToFitFullHeight; } else if (scaleToFit) { desiredScale = scaleNeededToFitHeight; } if (hasMinimumScale) { desiredScale = Math.max(desiredScale, Number(minimumScale)); } if (hasMaximumScale) { desiredScale = Math.min(desiredScale, Number(maximumScale)); //canCenterVertically = desiredScale>=scaleNeededToFitHeight && enableScaleUp==false; } desiredScale = self.getShortNumber(desiredScale); canCenterHorizontally = self.canCenterHorizontally(view, "height", enableScaleUp, desiredScale, minimumScale, maximumScale); canCenterVertically = self.canCenterVertically(view, "height", enableScaleUp, desiredScale, minimumScale, maximumScale); if (desiredScale>1 && (enableScaleUp || isSliderChange)) { transformValue = "scale(" + desiredScale + ")"; } else if (desiredScale>=1 && enableScaleUp==false) { transformValue = "scale(" + 1 + ")"; } else { transformValue = "scale(" + desiredScale + ")"; } if (self.centerHorizontally) { if (canCenterHorizontally) { translateX = "-50%"; leftPosition = "50%"; } else { translateX = "0"; leftPosition = "0"; } if (style.left != leftPosition) { style.left = leftPosition + ""; } if (canCenterHorizontally) { transformValue += " translateX(" + translateX+ ")"; } } if (self.centerVertically) { if (canCenterVertically) { translateY = "-50%"; topPosition = "50%"; } else { translateY = "0"; topPosition = "0"; } if (style.top != topPosition) { style.top = topPosition + ""; } if (canCenterVertically) { transformValue += " translateY(" + translateY+ ")"; } } style.transformOrigin = "0 0"; style.transform = transformValue; self.viewScale = desiredScale; self.viewToFitWidthScale = scaleNeededToFitWidth; self.viewToFitHeightScale = scaleNeededToFitHeight; self.viewLeft = leftPosition; self.viewTop = topPosition; return scaleNeededToFitHeight; } if (scaleToFitType=="fit") { //canCenterVertically = scaleNeededToFitHeight>=scaleNeededToFitWidth; //canCenterHorizontally = scaleNeededToFitWidth>=scaleNeededToFitHeight; canCenterVertically = scaleNeededToFitHeight>=scaleNeededToFit; canCenterHorizontally = scaleNeededToFitWidth>=scaleNeededToFit; if (hasMinimumScale) { desiredScale = Math.max(desiredScale, Number(minimumScale)); } desiredScale = self.getShortNumber(desiredScale); if (isSliderChange || scaleToFit==false) { canCenterVertically = scaleToFitFullHeight>=desiredScale; canCenterHorizontally = desiredScale<scaleToFitFullWidth; } else if (scaleToFit) { desiredScale = scaleNeededToFit; } transformValue = "scale(" + desiredScale + ")"; //canCenterHorizontally = self.canCenterHorizontally(view, "fit", false, desiredScale); //canCenterVertically = self.canCenterVertically(view, "fit", false, desiredScale); if (self.centerVertically) { if (canCenterVertically) { translateY = "-50%"; topPosition = "50%"; } else { translateY = "0"; topPosition = "0"; } if (style.top != topPosition) { style.top = topPosition + ""; } if (canCenterVertically) { transformValue += " translateY(" + translateY+ ")"; } } if (self.centerHorizontally) { if (canCenterHorizontally) { translateX = "-50%"; leftPosition = "50%"; } else { translateX = "0"; leftPosition = "0"; } if (style.left != leftPosition) { style.left = leftPosition + ""; } if (canCenterHorizontally) { transformValue += " translateX(" + translateX+ ")"; } } style.transformOrigin = "0 0"; style.transform = transformValue; self.viewScale = desiredScale; self.viewToFitWidthScale = scaleNeededToFitWidth; self.viewToFitHeightScale = scaleNeededToFitHeight; self.viewLeft = leftPosition; self.viewTop = topPosition; self.updateSliderValue(desiredScale); return desiredScale; } if (scaleToFitType=="default" || scaleToFitType=="") { desiredScale = 1; if (hasMinimumScale) { desiredScale = Math.max(desiredScale, Number(minimumScale)); } if (hasMaximumScale) { desiredScale = Math.min(desiredScale, Number(maximumScale)); } canCenterHorizontally = self.canCenterHorizontally(view, "none", false, desiredScale, minimumScale, maximumScale); canCenterVertically = self.canCenterVertically(view, "none", false, desiredScale, minimumScale, maximumScale); if (self.centerVertically) { if (canCenterVertically) { translateY = "-50%"; topPosition = "50%"; } else { translateY = "0"; topPosition = "0"; } if (style.top != topPosition) { style.top = topPosition + ""; } if (canCenterVertically) { transformValue += " translateY(" + translateY+ ")"; } } if (self.centerHorizontally) { if (canCenterHorizontally) { translateX = "-50%"; leftPosition = "50%"; } else { translateX = "0"; leftPosition = "0"; } if (style.left != leftPosition) { style.left = leftPosition + ""; } if (canCenterHorizontally) { transformValue += " translateX(" + translateX+ ")"; } else { transformValue += " translateX(" + 0 + ")"; } } style.transformOrigin = "0 0"; style.transform = transformValue; self.viewScale = desiredScale; self.viewToFitWidthScale = scaleNeededToFitWidth; self.viewToFitHeightScale = scaleNeededToFitHeight; self.viewLeft = leftPosition; self.viewTop = topPosition; self.updateSliderValue(desiredScale); return desiredScale; } } /** * Returns true if view can be centered horizontally * @param {HTMLElement} view view * @param {String} type type of scaling - width, height, all, none * @param {Boolean} scaleUp if scale up enabled * @param {Number} scale target scale value */ self.canCenterHorizontally = function(view, type, scaleUp, scale, minimumScale, maximumScale) { var scaleNeededToFit = self.getViewFitToViewportScale(view, scaleUp); var scaleNeededToFitHeight = self.getViewFitToViewportHeightScale(view, scaleUp); var scaleNeededToFitWidth = self.getViewFitToViewportWidthScale(view, scaleUp); var canCenter = false; var minScale; type = type==null ? "none" : type; scale = scale==null ? scale : scaleNeededToFitWidth; scaleUp = scaleUp == null ? false : scaleUp; if (type=="width") { if (scaleUp && maximumScale==null) { canCenter = false; } else if (scaleNeededToFitWidth>=1) { canCenter = true; } } else if (type=="height") { minScale = Math.min(1, scaleNeededToFitHeight); if (minimumScale!="" && maximumScale!="") { minScale = Math.max(minimumScale, Math.min(maximumScale, scaleNeededToFitHeight)); } else { if (minimumScale!="") { minScale = Math.max(minimumScale, scaleNeededToFitHeight); } if (maximumScale!="") { minScale = Math.max(minimumScale, Math.min(maximumScale, scaleNeededToFitHeight)); } } if (scaleUp && maximumScale=="") { canCenter = false; } else if (scaleNeededToFitWidth>=minScale) { canCenter = true; } } else if (type=="fit") { canCenter = scaleNeededToFitWidth>=scaleNeededToFit; } else { if (scaleUp) { canCenter = false; } else if (scaleNeededToFitWidth>=1) { canCenter = true; } } self.horizontalScrollbarsNeeded = canCenter; return canCenter; } /** * Returns true if view can be centered horizontally * @param {HTMLElement} view view to scale * @param {String} type type of scaling * @param {Boolean} scaleUp if scale up enabled * @param {Number} scale target scale value */ self.canCenterVertically = function(view, type, scaleUp, scale, minimumScale, maximumScale) { var scaleNeededToFit = self.getViewFitToViewportScale(view, scaleUp); var scaleNeededToFitWidth = self.getViewFitToViewportWidthScale(view, scaleUp); var scaleNeededToFitHeight = self.getViewFitToViewportHeightScale(view, scaleUp); var canCenter = false; var minScale; type = type==null ? "none" : type; scale = scale==null ? 1 : scale; scaleUp = scaleUp == null ? false : scaleUp; if (type=="width") { canCenter = scaleNeededToFitHeight>=scaleNeededToFitWidth; } else if (type=="height") { minScale = Math.max(minimumScale, Math.min(maximumScale, scaleNeededToFit)); canCenter = scaleNeededToFitHeight>=minScale; } else if (type=="fit") { canCenter = scaleNeededToFitHeight>=scaleNeededToFit; } else { if (scaleUp) { canCenter = false; } else if (scaleNeededToFitHeight>=1) { canCenter = true; } } self.verticalScrollbarsNeeded = canCenter; return canCenter; } self.getViewFitToViewportScale = function(view, scaleUp) { var enableScaleUp = scaleUp; var availableWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; var availableHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; var elementWidth = parseFloat(getComputedStyle(view, "style").width); var elementHeight = parseFloat(getComputedStyle(view, "style").height); var newScale = 1; // if element is not added to the document computed values are NaN if (isNaN(elementWidth) || isNaN(elementHeight)) { return newScale; } availableWidth -= self.horizontalPadding; availableHeight -= self.verticalPadding; if (enableScaleUp) { newScale = Math.min(availableHeight/elementHeight, availableWidth/elementWidth); } else if (elementWidth > availableWidth || elementHeight > availableHeight) { newScale = Math.min(availableHeight/elementHeight, availableWidth/elementWidth); } return newScale; } self.getViewFitToViewportWidthScale = function(view, scaleUp) { // need to get browser viewport width when element var isParentWindow = view && view.parentNode && view.parentNode===document.body; var enableScaleUp = scaleUp; var availableWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; var elementWidth = parseFloat(getComputedStyle(view, "style").width); var newScale = 1; // if element is not added to the document computed values are NaN if (isNaN(elementWidth)) { return newScale; } availableWidth -= self.horizontalPadding; if (enableScaleUp) { newScale = availableWidth/elementWidth; } else if (elementWidth > availableWidth) { newScale = availableWidth/elementWidth; } return newScale; } self.getViewFitToViewportHeightScale = function(view, scaleUp) { var enableScaleUp = scaleUp; var availableHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; var elementHeight = parseFloat(getComputedStyle(view, "style").height); var newScale = 1; // if element is not added to the document computed values are NaN if (isNaN(elementHeight)) { return newScale; } availableHeight -= self.verticalPadding; if (enableScaleUp) { newScale = availableHeight/elementHeight; } else if (elementHeight > availableHeight) { newScale = availableHeight/elementHeight; } return newScale; } self.keypressHandler = function(event) { var rightKey = 39; var leftKey = 37; // listen for both events if (event.type=="keypress") { window.removeEventListener("keyup", self.keypressHandler); } else { window.removeEventListener("keypress", self.keypressHandler); } if (self.showNavigationControls) { if (self.navigationOnKeypress) { if (event.keyCode==rightKey) { self.nextView(); } if (event.keyCode==leftKey) { self.previousView(); } } } else if (self.navigationOnKeypress) { if (event.keyCode==rightKey) { self.nextView(); } if (event.keyCode==leftKey) { self.previousView(); } } } /////////////////////////////////// // GENERAL FUNCTIONS /////////////////////////////////// self.getViewById = function(id) { id = id ? id.replace("#", "") : ""; var view = self.viewIds.indexOf(id)!=-1 && self.getElement(id); return view; } self.getViewIds = function() { var viewIds = self.getViewPreferenceValue(document.body, self.prefix + "view-ids"); var viewId = null; viewIds = viewIds!=null && viewIds!="" ? viewIds.split(",") : []; if (viewIds.length==0) { viewId = self.getViewPreferenceValue(document.body, self.prefix + "view-id"); viewIds = viewId ? [viewId] : []; } return viewIds; } self.getInitialViewId = function() { var viewId = self.getViewPreferenceValue(document.body, self.prefix + "view-id"); return viewId; } self.getApplicationStylesheet = function() { var stylesheetId = self.getViewPreferenceValue(document.body, self.prefix + "stylesheet-id"); self.applicationStylesheet = document.getElementById("applicationStylesheet"); return self.applicationStylesheet.sheet; } self.getVisibleView = function() { var viewIds = self.getViewIds(); for (var i=0;i<viewIds.length;i++) { var viewId = viewIds[i].replace(/[#?.?](.*)/, "$" + "1"); var view = self.getElement(viewId); var postName = "_Class"; if (view==null && viewId && viewId.lastIndexOf(postName)!=-1) { view = self.getElement(viewId.replace(postName, "")); } if (view) { var display = getComputedStyle(view).display; if (display=="block" || display=="flex") { return view; } } } return null; } self.getVisibleViews = function() { var viewIds = self.getViewIds(); var views = []; for (var i=0;i<viewIds.length;i++) { var viewId = viewIds[i].replace(/[#?.?](.*)/, "$" + "1"); var view = self.getElement(viewId); var postName = "_Class"; if (view==null && viewId && viewId.lastIndexOf(postName)!=-1) { view = self.getElement(viewId.replace(postName, "")); } if (view) { var display = getComputedStyle(view).display; if (display=="none") { continue; } if (display=="block" || display=="flex") { views.push(view); } } } return views; } self.getStateNameByViewId = function(id) { var state = self.viewsDictionary[id]; return state && state.stateName; } self.getMatchingViews = function(ids) { var views = self.addedViews.slice(0); var matchingViews = []; if (self.showByMediaQuery) { for (let index = 0; index < views.length; index++) { var viewId = views[index]; var state = self.viewsDictionary[viewId]; var rule = state && state.rule; var matchResults = window.matchMedia(rule.conditionText); var view = self.views[viewId]; if (matchResults.matches) { if (ids==true) { matchingViews.push(viewId); } else { matchingViews.push(view); } } } } return matchingViews; } self.ruleMatchesQuery = function(rule) { var result = window.matchMedia(rule.conditionText); return result.matches; } self.getViewsByStateName = function(stateName, matchQuery) { var views = self.addedViews.slice(0); var matchingViews = []; if (self.showByMediaQuery) { // find state name for (let index = 0; index < views.length; index++) { var viewId = views[index]; var state = self.viewsDictionary[viewId]; var rule = state.rule; var mediaRule = state.mediaRule; var view = self.views[viewId]; var viewStateName = self.getStyleRuleValue(mediaRule, self.STATE_NAME, state); var stateFoundAtt = view.getAttribute(self.STATE_NAME)==state; var matchesResults = false; if (viewStateName==stateName) { if (matchQuery) { matchesResults = self.ruleMatchesQuery(rule); if (matchesResults) { matchingViews.push(view); } } else { matchingViews.push(view); } } } } return matchingViews; } self.getInitialView = function() { var viewId = self.getInitialViewId(); viewId = viewId.replace(/[#?.?](.*)/, "$" + "1"); var view = self.getElement(viewId); var postName = "_Class"; if (view==null && viewId && viewId.lastIndexOf(postName)!=-1) { view = self.getElement(viewId.replace(postName, "")); } return view; } self.getViewIndex = function(view) { var viewIds = self.getViewIds(); var id = view ? view.id : null; var index = id && viewIds ? viewIds.indexOf(id) : -1; return index; } self.syncronizeViewToURL = function() { var fragment = self.getHashFragment(); if (self.showByMediaQuery) { var stateName = fragment; if (stateName==null || stateName=="") { var initialView = self.getInitialView(); stateName = initialView ? self.getStateNameByViewId(initialView.id) : null; } self.showMediaQueryViewsByState(stateName); return; } var view = self.getViewById(fragment); var index = view ? self.getViewIndex(view) : 0; if (index==-1) index = 0; var currentView = self.hideViews(index); if (self.supportsPopState && currentView) { if (fragment==null) { window.history.replaceState({name:currentView.id}, null, "#"+ currentView.id); } else { window.history.pushState({name:currentView.id}, null, "#"+ currentView.id); } } self.setViewVariables(view); return view; } /** * Set the currentView or currentOverlay properties and set the lastView or lastOverlay properties */ self.setViewVariables = function(view, overlay, parentView) { if (view) { if (self.currentView) { self.lastView = self.currentView; } self.currentView = view; } if (overlay) { if (self.currentOverlay) { self.lastOverlay = self.currentOverlay; } self.currentOverlay = overlay; } } self.getViewPreferenceBoolean = function(view, property, altValue) { var computedStyle = window.getComputedStyle(view); var value = computedStyle.getPropertyValue(property); var type = typeof value; if (value=="true" || (type=="string" && value.indexOf("true")!=-1)) { return true; } else if (value=="" && arguments.length==3) { return altValue; } return false; } self.getViewPreferenceValue = function(view, property, defaultValue) { var value = window.getComputedStyle(view).getPropertyValue(property); if (value===undefined) { return defaultValue; } value = value.replace(/^[s"]*/, ""); value = value.replace(/[s"]*$/, ""); value = value.replace(/^[s"]*(.*?)[s"]*$/, function (match, capture) { return capture; }); return value; } self.getStyleRuleValue = function(cssRule, property) { var value = cssRule ? cssRule.style.getPropertyValue(property) : null; if (value===undefined) { return null; } value = value.replace(/^[s"]*/, ""); value = value.replace(/[s"]*$/, ""); value = value.replace(/^[s"]*(.*?)[s"]*$/, function (match, capture) { return capture; }); return value; } /** * Get the first defined value of property. Returns empty string if not defined * @param {String} id id of element * @param {String} property */ self.getCSSPropertyValueForElement = function(id, property) { var styleSheets = document.styleSheets; var numOfStylesheets = styleSheets.length; var values = []; var selectorIDText = "#" + id; var selectorClassText = "." + id + "_Class"; var value; for(var i=0;i<numOfStylesheets;i++) { var styleSheet = styleSheets[i]; var cssRules = self.getStylesheetRules(styleSheet); var numOfCSSRules = cssRules.length; var cssRule; for (var j=0;j<numOfCSSRules;j++) { cssRule = cssRules[j]; if (cssRule.media) { var mediaRules = cssRule.cssRules; var numOfMediaRules = mediaRules ? mediaRules.length : 0; for(var k=0;k<numOfMediaRules;k++) { var mediaRule = mediaRules[k]; if (mediaRule.selectorText==selectorIDText || mediaRule.selectorText==selectorClassText) { if (mediaRule.style && mediaRule.style.getPropertyValue(property)!="") { value = mediaRule.style.getPropertyValue(property); values.push(value); } } } } else { if (cssRule.selectorText==selectorIDText || cssRule.selectorText==selectorClassText) { if (cssRule.style && cssRule.style.getPropertyValue(property)!="") { value = cssRule.style.getPropertyValue(property); values.push(value); } } } } } return values.pop(); } self.getIsStyleDefined = function(id, property) { var value = self.getCSSPropertyValueForElement(id, property); return value!==undefined && value!=""; } self.collectViews = function() { var viewIds = self.getViewIds(); for (let index = 0; index < viewIds.length; index++) { const id = viewIds[index]; const view = self.getElement(id); self.views[id] = view; } self.viewIds = viewIds; } self.collectOverlays = function() { var viewIds = self.getViewIds(); var ids = []; for (let index = 0; index < viewIds.length; index++) { const id = viewIds[index]; const view = self.getViewById(id); const isOverlay = view && self.isOverlay(view); if (isOverlay) { ids.push(id); self.overlays[id] = view; } } self.overlayIds = ids; } self.collectMediaQueries = function() { var viewIds = self.getViewIds(); var styleSheet = self.getApplicationStylesheet(); var cssRules = self.getStylesheetRules(styleSheet); var numOfCSSRules = cssRules ? cssRules.length : 0; var cssRule; var id = viewIds.length ? viewIds[0]: ""; // single view var selectorIDText = "#" + id; var selectorClassText = "." + id + "_Class"; var viewsNotFound = viewIds.slice(); var viewsFound = []; var selectorText = null; var property = self.prefix + "view-id"; var stateName = self.prefix + "state"; var stateValue = null; var view = null; for (var j=0;j<numOfCSSRules;j++) { cssRule = cssRules[j]; if (cssRule.media) { var mediaRules = cssRule.cssRules; var numOfMediaRules = mediaRules ? mediaRules.length : 0; var mediaViewInfoFound = false; var mediaId = null; for(var k=0;k<numOfMediaRules;k++) { var mediaRule = mediaRules[k]; selectorText = mediaRule.selectorText; if (selectorText==".mediaViewInfo" && mediaViewInfoFound==false) { mediaId = self.getStyleRuleValue(mediaRule, property); stateValue = self.getStyleRuleValue(mediaRule, stateName); selectorIDText = "#" + mediaId; selectorClassText = "." + mediaId + "_Class"; view = self.getElement(mediaId); // prevent duplicates from load and domcontentloaded events if (self.addedViews.indexOf(mediaId)==-1) { self.addView(view, mediaId, cssRule, mediaRule, stateValue); } viewsFound.push(mediaId); if (viewsNotFound.indexOf(mediaId)!=-1) { viewsNotFound.splice(viewsNotFound.indexOf(mediaId)); } mediaViewInfoFound = true; } if (selectorIDText==selectorText || selectorClassText==selectorText) { var styleObject = self.viewsDictionary[mediaId]; if (styleObject) { styleObject.styleDeclaration = mediaRule; } break; } } } else { selectorText = cssRule.selectorText; if (selectorText==null) continue; selectorText = selectorText.replace(/[#|s|*]?/g, ""); if (viewIds.indexOf(selectorText)!=-1) { view = self.getElement(selectorText); self.addView(view, selectorText, cssRule, null, stateValue); if (viewsNotFound.indexOf(selectorText)!=-1) { viewsNotFound.splice(viewsNotFound.indexOf(selectorText)); } break; } } } if (viewsNotFound.length) { console.log("Could not find the following views:" + viewsNotFound.join(",") + ""); console.log("Views found:" + viewsFound.join(",") + ""); } } /** * Adds a view * @param {HTMLElement} view view element * @param {String} id id of view element * @param {CSSRule} cssRule of view element * @param {CSSMediaRule} mediaRule media rule of view element * @param {String} stateName name of state if applicable **/ self.addView = function(view, viewId, cssRule, mediaRule, stateName) { var viewData = {}; viewData.name = viewId; viewData.rule = cssRule; viewData.id = viewId; viewData.mediaRule = mediaRule; viewData.stateName = stateName; self.views.push(viewData); self.addedViews.push(viewId); self.viewsDictionary[viewId] = viewData; self.mediaQueryDictionary[viewId] = cssRule; } self.hasView = function(name) { if (self.addedViews.indexOf(name)!=-1) { return true; } return false; } /** * Go to view by id. Views are added in addView() * @param {String} id id of view in current * @param {Boolean} maintainPreviousState if true then do not hide other views * @param {String} parent id of parent view **/ self.goToView = function(id, maintainPreviousState, parent) { var state = self.viewsDictionary[id]; if (state) { if (maintainPreviousState==false || maintainPreviousState==null) { self.hideViews(); } self.enableMediaQuery(state.rule); self.updateViewLabel(); self.updateURL(); } else { var event = new Event(self.STATE_NOT_FOUND); self.stateName = id; window.dispatchEvent(event); } } /** * Go to the view in the event targets CSS variable **/ self.goToTargetView = function(event) { var button = event.currentTarget; var buttonComputedStyles = getComputedStyle(button); var actionTargetValue = buttonComputedStyles.getPropertyValue(self.prefix+"action-target").trim(); var animation = buttonComputedStyles.getPropertyValue(self.prefix+"animation").trim(); var targetType = buttonComputedStyles.getPropertyValue(self.prefix+"action-type").trim(); var targetView = self.application ? null : self.getElement(actionTargetValue); var targetState = targetView ? self.getStateNameByViewId(targetView.id) : null; var actionTargetStyles = targetView ? targetView.style : null; var state = self.viewsDictionary[actionTargetValue]; // navigate to page if (self.application==false || targetType=="page") { document.location.href = "./" + actionTargetValue; return; } // if view is found if (targetView) { if (self.currentOverlay) { self.removeOverlay(false); } if (self.showByMediaQuery) { var stateName = targetState; if (stateName==null || stateName=="") { var initialView = self.getInitialView(); stateName = initialView ? self.getStateNameByViewId(initialView.id) : null; } self.showMediaQueryViewsByState(stateName, event); return; } // add animation set in event target style declaration if (animation && self.supportAnimations) { self.crossFade(self.currentView, targetView, false, animation); } else { self.setViewVariables(self.currentView); self.hideViews(); self.enableMediaQuery(state.rule); self.scaleViewIfNeeded(targetView); self.centerView(targetView); self.updateViewLabel(); self.updateURL(); } } else { var stateEvent = new Event(self.STATE_NOT_FOUND); self.stateName = name; window.dispatchEvent(stateEvent); } event.stopImmediatePropagation(); } /** * Cross fade between views **/ self.crossFade = function(from, to, update, animation) { var targetIndex = to.parentNode var fromIndex = Array.prototype.slice.call(from.parentElement.children).indexOf(from); var toIndex = Array.prototype.slice.call(to.parentElement.children).indexOf(to); if (from.parentNode==to.parentNode) { var reverse = self.getReverseAnimation(animation); var duration = self.getAnimationDuration(animation, true); // if target view is above (higher index) // then fade in target view // and after fade in then hide previous view instantly if (fromIndex<toIndex) { self.setElementAnimation(from, null); self.setElementAnimation(to, null); self.showViewByMediaQuery(to); self.fadeIn(to, update, animation); setTimeout(function() { self.setElementAnimation(to, null); self.setElementAnimation(from, null); self.hideView(from); self.updateURL(); self.setViewVariables(to); self.updateViewLabel(); }, duration) } // if target view is on bottom // then show target view instantly // and fade out current view else if (fromIndex>toIndex) { self.setElementAnimation(to, null); self.setElementAnimation(from, null); self.showViewByMediaQuery(to); self.fadeOut(from, update, reverse); setTimeout(function() { self.setElementAnimation(to, null); self.setElementAnimation(from, null); self.hideView(from); self.updateURL(); self.setViewVariables(to); }, duration) } } } self.fadeIn = function(element, update, animation) { self.showViewByMediaQuery(element); if (update) { self.updateURL(element); element.addEventListener("animationend", function(event) { element.style.animation = null; self.setViewVariables(element); self.updateViewLabel(); element.removeEventListener("animationend", arguments.callee); }); } self.setElementAnimation(element, null); element.style.animation = animation; } self.fadeOutCurrentView = function(animation, update) { if (self.currentView) { self.fadeOut(self.currentView, update, animation); } if (self.currentOverlay) { self.fadeOut(self.currentOverlay, update, animation); } } self.fadeOut = function(element, update, animation) { if (update) { element.addEventListener("animationend", function(event) { element.style.animation = null; self.hideView(element); element.removeEventListener("animationend", arguments.callee); }); } element.style.animationPlayState = "paused"; element.style.animation = animation; element.style.animationPlayState = "running"; } self.getReverseAnimation = function(animation) { if (animation && animation.indexOf("reverse")==-1) { animation += " reverse"; } return animation; } /** * Get duration in animation string * @param {String} animation animation value * @param {Boolean} inMilliseconds length in milliseconds if true */ self.getAnimationDuration = function(animation, inMilliseconds) { var duration = 0; var expression = /.+(d.d)s.+/; if (animation && animation.match(expression)) { duration = parseFloat(animation.replace(expression, "$" + "1")); if (duration && inMilliseconds) duration = duration * 1000; } return duration; } self.setElementAnimation = function(element, animation, priority) { element.style.setProperty("animation", animation, "important"); } self.getElement = function(id) { id = id ? id.trim() : id; var element = id ? document.getElementById(id) : null; return element; } self.getElementById = function(id) { id = id ? id.trim() : id; var element = id ? document.getElementById(id) : null; return element; } self.getElementByClass = function(className) { className = className ? className.trim() : className; var elements = document.getElementsByClassName(className); return elements.length ? elements[0] : null; } self.resizeHandler = function(event) { if (self.showByMediaQuery) { if (self.enableDeepLinking) { var stateName = self.getHashFragment(); if (stateName==null || stateName=="") { var initialView = self.getInitialView(); stateName = initialView ? self.getStateNameByViewId(initialView.id) : null; } self.showMediaQueryViewsByState(stateName, event); } } else { var visibleViews = self.getVisibleViews(); for (let index = 0; index < visibleViews.length; index++) { var view = visibleViews[index]; self.scaleViewIfNeeded(view); } } window.dispatchEvent(new Event(self.APPLICATION_RESIZE)); } self.scaleViewIfNeeded = function(view) { if (self.scaleViewsOnResize) { if (view==null) { view = self.getVisibleView(); } var isViewScaled = view.getAttributeNS(null, self.SIZE_STATE_NAME)=="false" ? false : true; if (isViewScaled) { self.scaleViewToFit(view, true); } else { self.scaleViewToActualSize(view); } } else if (view) { self.centerView(view); } } self.centerView = function(view) { if (self.scaleViewsToFit) { self.scaleViewToFit(view, true); } else { self.scaleViewToActualSize(view); // for centering support for now } } self.preventDoubleClick = function(event) { event.stopImmediatePropagation(); } self.getHashFragment = function() { var value = window.location.hash ? window.location.hash.replace("#", "") : ""; return value; } self.showBlockElement = function(view) { view.style.display = "block"; } self.hideElement = function(view) { view.style.display = "none"; } self.showStateFunction = null; self.showMediaQueryViewsByState = function(state, event) { // browser will hide and show by media query (small, medium, large) // but if multiple views exists at same size user may want specific view // if showStateFunction is defined that is called with state fragment and user can show or hide each media matching view by returning true or false // if showStateFunction is not defined and state is defined and view has a defined state that matches then show that and hide other matching views // if no state is defined show view // an viewChanging event is dispatched before views are shown or hidden that can be prevented // get all matched queries // if state name is specified then show that view and hide other views // if no state name is defined then show var matchedViews = self.getMatchingViews(); var matchMediaQuery = true; var foundViews = self.getViewsByStateName(state, matchMediaQuery); var showViews = []; var hideViews = []; // loop views that match media query for (let index = 0; index < matchedViews.length; index++) { var view = matchedViews[index]; // let user determine visible view if (self.showStateFunction!=null) { if (self.showStateFunction(view, state)) { showViews.push(view); } else { hideViews.push(view); } } // state was defined so check if view matches state else if (foundViews.length) { if (foundViews.indexOf(view)!=-1) { showViews.push(view); } else { hideViews.push(view); } } // if no state names are defined show view (define unused state name to exclude) else if (state==null || state=="") { showViews.push(view); } } if (showViews.length) { var viewChangingEvent = new Event(self.VIEW_CHANGING); viewChangingEvent.showViews = showViews; viewChangingEvent.hideViews = hideViews; window.dispatchEvent(viewChangingEvent); if (viewChangingEvent.defaultPrevented==false) { for (var index = 0; index < hideViews.length; index++) { var view = hideViews[index]; if (self.isOverlay(view)) { self.removeOverlay(view); } else { self.hideElement(view); } } for (var index = 0; index < showViews.length; index++) { var view = showViews[index]; if (index==showViews.length-1) { self.clearDisplay(view); self.setViewOptions(view); self.setViewVariables(view); self.centerView(view); self.updateURLState(view, state); } } } var viewChangeEvent = new Event(self.VIEW_CHANGE); viewChangeEvent.showViews = showViews; viewChangeEvent.hideViews = hideViews; window.dispatchEvent(viewChangeEvent); } } self.clearDisplay = function(view) { view.style.setProperty("display", null); } self.hashChangeHandler = function(event) { var fragment = self.getHashFragment(); var view = self.getViewById(fragment); if (self.showByMediaQuery) { var stateName = fragment; if (stateName==null || stateName=="") { var initialView = self.getInitialView(); stateName = initialView ? self.getStateNameByViewId(initialView.id) : null; } self.showMediaQueryViewsByState(stateName); } else { if (view) { self.hideViews(); self.showView(view); self.setViewVariables(view); self.updateViewLabel(); window.dispatchEvent(new Event(self.VIEW_CHANGE)); } else { window.dispatchEvent(new Event(self.VIEW_NOT_FOUND)); } } } self.popStateHandler = function(event) { var state = event.state; var fragment = state ? state.name : window.location.hash; var view = self.getViewById(fragment); if (view) { self.hideViews(); self.showView(view); self.updateViewLabel(); } else { window.dispatchEvent(new Event(self.VIEW_NOT_FOUND)); } } self.doubleClickHandler = function(event) { var view = self.getVisibleView(); var scaleValue = view ? self.getViewScaleValue(view) : 1; var scaleNeededToFit = view ? self.getViewFitToViewportScale(view) : 1; var scaleNeededToFitWidth = view ? self.getViewFitToViewportWidthScale(view) : 1; var scaleNeededToFitHeight = view ? self.getViewFitToViewportHeightScale(view) : 1; var scaleToFitType = self.scaleToFitType; // Three scenarios // - scale to fit on double click // - set scale to actual size on double click // - switch between scale to fit and actual page size if (scaleToFitType=="width") { scaleNeededToFit = scaleNeededToFitWidth; } else if (scaleToFitType=="height") { scaleNeededToFit = scaleNeededToFitHeight; } // if scale and actual size enabled then switch between if (self.scaleToFitOnDoubleClick && self.actualSizeOnDoubleClick) { var isViewScaled = view.getAttributeNS(null, self.SIZE_STATE_NAME); var isScaled = false; // if scale is not 1 then view needs scaling if (scaleNeededToFit!=1) { // if current scale is at 1 it is at actual size // scale it to fit if (scaleValue==1) { self.scaleViewToFit(view); isScaled = true; } else { // scale is not at 1 so switch to actual size self.scaleViewToActualSize(view); isScaled = false; } } else { // view is smaller than viewport // so scale to fit() is scale actual size // actual size and scaled size are the same // but call scale to fit to retain centering self.scaleViewToFit(view); isScaled = false; } view.setAttributeNS(null, self.SIZE_STATE_NAME, isScaled+""); isViewScaled = view.getAttributeNS(null, self.SIZE_STATE_NAME); } else if (self.scaleToFitOnDoubleClick) { self.scaleViewToFit(view); } else if (self.actualSizeOnDoubleClick) { self.scaleViewToActualSize(view); } } self.scaleViewToFit = function(view) { return self.setViewScaleValue(view, true); } self.scaleViewToActualSize = function(view) { self.setViewScaleValue(view, false, 1); } self.onloadHandler = function(event) { self.initialize(); } self.setElementHTML = function(id, value) { var element = self.getElement(id); element.innerHTML = value; } self.getStackArray = function(error) { var value = ""; if (error==null) { try { error = new Error("Stack"); } catch (e) { } } if ("stack" in error) { value = error.stack; var methods = value.split(/n/g); var newArray = methods ? methods.map(function (value, index, array) { value = value.replace(/@.*/,""); return value; }) : null; if (newArray && newArray[0].includes("getStackTrace")) { newArray.shift(); } if (newArray && newArray[0].includes("getStackArray")) { newArray.shift(); } if (newArray && newArray[0]=="") { newArray.shift(); } return newArray; } return null; } self.log = function(value) { console.log.apply(this, [value]); } // initialize on load // sometimes the body size is 0 so we call this now and again later window.addEventListener("load", self.onloadHandler); window.document.addEventListener("DOMContentLoaded", self.onloadHandler); } window.application = new Application(); </script> </head> <style> <body> <div id="Explore__1"> <div id="Scroll_Group_1"> background-image: <img id="robin-noguier-3pC6oFadbF8-unsp" src="robin-noguier-3pC6oFadbF8-unsp.png" srcset="robin-noguier-3pC6oFadbF8-unsp.png 1x, robin-noguier-3pC6oFadbF8-unsp@2x.png 2x"> </div> </body> </style> <svg class="Rectangle_4"> <rect id="Rectangle_4" rx="0" ry="0" x="0" y="0" width="140" height="1080"> </rect> </svg> <div id="Travel"> <div id="Explore_Indonesia"> <h6>Explore Indonesia</h6> </div> <svg class="Rectangle_10"> <rect id="Rectangle_10" rx="62" ry="62" x="0" y="0" width="398" height="124"> </rect> </svg> <div id="Start_Traveling"> <span>Start Traveling</span> </div> </div> <div id="Barre"> <svg class="Line_1" viewBox="0 0 3 130"> <path id="Line_1" d="M 0 0 L 3 130"> </path> </svg> <svg class="Line_2" viewBox="0 0 3 304"> <path id="Line_2" d="M 0 0 L 3 304"> </path> </svg> <div id="n_1"> <span>01</span> </div> </div> <div id="Footer_000"> <svg class="Rectangle_5"> <rect id="Rectangle_5" rx="0" ry="0" x="0" y="0" width="1779" height="210"> </rect> </svg> <svg class="Rectangle_6"> <rect id="Rectangle_6" rx="0" ry="0" x="0" y="0" width="410.5" height="210"> </rect> </svg> <svg class="Rectangle_7"> <rect id="Rectangle_7" rx="0" ry="0" x="0" y="0" width="410.5" height="210"> </rect> </svg> <svg class="Rectangle_8"> <rect id="Rectangle_8" rx="0" ry="0" x="0" y="0" width="410.5" height="210"> </rect> </svg> <img id="hanandito-adi-kieIGjdPTno-unsp" src="hanandito-adi-kieIGjdPTno-unsp.png" srcset="hanandito-adi-kieIGjdPTno-unsp.png 1x, hanandito-adi-kieIGjdPTno-unsp@2x.png 2x"> </div> <div id="Footer_1"> <div id="n_1_z"> <span>01</span> </div> <div id="Dieng_Plateau"> <span>Dieng Plateau</span> </div> <div id="Moutain_views_jungles_and_dist"> <span>Moutain views, jungles and distant village.</span> </div> </div> <div id="Footer_2"> <div id="One_of_the_most_beautiful_neib"> <span>One of the most beautiful neiborhoods in Bali.</span> </div> <div id="n_2"> <span>02</span> </div> <div id="Ubud"> <span>Ubud</span> </div> </div> <div id="Footer_3"> <div id="This_volcano_has_become_one_of"> <span>This volcano has become one of the most iconic sights.</span> </div> <div id="n_3"> <span>03</span> </div> <div id="Mount_Bromo"> <span>Mount Bromo</span> </div> </div> <div id="Social"> <div id="In"> <span>In</span> </div> <div id="Tw"> <span>Tw</span> </div> <div id="Fb"> <span>Fb</span> </div> </div> <div id="Play"> <svg class="Ellipse_1"> <ellipse id="Ellipse_1" rx="36" ry="36" cx="36" cy="36"> </ellipse> </svg> <svg class="Icon_awesome-play" viewBox="0 0.002 31.499 36.001"> <path id="Icon_awesome-play" d="M 29.84062576293945 15.09609413146973 L 5.090624809265137 0.464062511920929 C 3.079687595367432 -0.7242187261581421 0 0.428906261920929 0 3.367968797683716 L 0 32.625 C 0 35.26171875 2.861718654632568 36.85078048706055 5.090624809265137 35.52890777587891 L 29.84062576293945 20.90390777587891 C 32.04843902587891 19.60312652587891 32.05546951293945 16.39687728881836 29.84062576293945 15.09609508514404 Z"> </path> </svg> </div> <div id="Navigation"> <div id="Destinations"> <span>Destinations</span> </div> <div id="Experiences"> <span>Experiences</span> </div> <div id="About"> <span>About</span> </div> <div id="Gallery"> <span>Gallery</span> </div> <svg class="Rectangle_1"> <rect id="Rectangle_1" rx="0" ry="0" x="0" y="0" width="24" height="24"> </rect> </svg> <svg class="Rectangle_2"> <rect id="Rectangle_2" rx="0" ry="0" x="0" y="0" width="24" height="24"> </rect> </svg> <svg class="Rectangle_3"> <rect id="Rectangle_3" rx="0" ry="0" x="0" y="0" width="24" height="24"> </rect> </svg> <svg class="Icon_awesome-search" viewBox="0 0 20 19"> <path id="Icon_awesome-search" d="M 19.72800445556641 16.42671585083008 L 15.83318901062012 12.72727203369141 C 15.65739440917969 12.5602970123291 15.41909599304199 12.46753215789795 15.16907787322998 12.46753215789795 L 14.53231239318848 12.46753215789795 C 15.61051559448242 11.15769958496094 16.25118827819824 9.51020336151123 16.25118827819824 7.717996120452881 C 16.25118827819824 3.454545497894287 12.61420345306396 0 8.125594139099121 0 C 3.63698410987854 0 0 3.454545497894287 0 7.717996120452881 C 0 11.98144721984863 3.636984825134277 15.43599224090576 8.125594139099121 15.43599224090576 C 10.01245021820068 15.43599224090576 11.74695301055908 14.82745742797852 13.1259593963623 13.8033390045166 L 13.1259593963623 14.40816307067871 C 13.1259593963623 14.64564037322998 13.22362327575684 14.87198448181152 13.39941692352295 15.03896045684814 L 17.29423332214355 18.7384033203125 C 17.66144752502441 19.08719825744629 18.25524139404297 19.08719825744629 18.6185474395752 18.7384033203125 L 19.72409820556641 17.68831062316895 C 20.0913143157959 17.33951568603516 20.0913143157959 16.77550888061523 19.72800445556641 16.42671585083008 Z M 8.125594139099121 12.46753215789795 C 5.363673210144043 12.46753215789795 3.125228404998779 10.34508323669434 3.125228404998779 7.717996120452881 C 3.125228404998779 5.094619274139404 5.359766960144043 2.968460083007812 8.125594139099121 2.968460083007812 C 10.8875150680542 2.968460083007812 13.1259593963623 5.090909004211426 13.1259593963623 7.717996120452881 C 13.1259593963623 10.3413724899292 10.8914213180542 12.46753215789795 8.125594139099121 12.46753215789795 Z"> </path> </svg> <svg class="Icon_awesome-map-marker-alt" viewBox="0 0 20 19"> <path id="Icon_awesome-map-marker-alt" d="M 8.972290992736816 18.61665344238281 C 1.404687404632568 10.79997539520264 0 9.997745513916016 0 7.124997615814209 C 0 3.189957857131958 4.477135181427002 0 10 0 C 15.52286434173584 0 20 3.189957857131958 20 7.124997615814209 C 20 9.997745513916016 18.59531211853027 10.79997539520264 11.02770805358887 18.61665534973145 C 10.53109264373779 19.12779998779297 9.468853950500488 19.12776374816895 8.972290992736816 18.61665534973145 Z M 10 10.09374713897705 C 12.30119705200195 10.09374713897705 14.16666603088379 8.76460075378418 14.16666603088379 7.124997615814209 C 14.16666603088379 5.485394954681396 12.30119705200195 4.156248569488525 10 4.156248569488525 C 7.69880199432373 4.156248569488525 5.833333015441895 5.485394954681396 5.833333015441895 7.124997615814209 C 5.833333015441895 8.76460075378418 7.69880199432373 10.09374713897705 10 10.09374713897705 Z"> </path> </svg> <div id="Icon_feather-grid"> <svg class="Path_1" viewBox="4.5 4.5 10.5 10.5"> <path id="Path_1" d="M 4.5 4.5 L 15 4.5 L 15 15 L 4.5 15 L 4.5 4.5 Z"> </path> </svg> <svg class="Path_2" viewBox="21 4.5 10.5 10.5"> <path id="Path_2" d="M 21 4.5 L 31.5 4.5 L 31.5 15 L 21 15 L 21 4.5 Z"> </path> </svg> <svg class="Path_3" viewBox="21 21 10.5 10.5"> <path id="Path_3" d="M 21 21 L 31.5 21 L 31.5 31.5 L 21 31.5 L 21 21 Z"> </path> </svg> <svg class="Path_4" viewBox="4.5 21 10.5 10.5"> <path id="Path_4" d="M 4.5 21 L 15 21 L 15 31.5 L 4.5 31.5 L 4.5 21 Z"> </path> </svg> </div> </div> </div> </body> </html>
|