/* * NavigatorController.js * Keynote HTML Player * * Created by Tungwei Cheng * Copyright (c) 2012-2013 Apple Inc. All rights reserved. */ var NavigatorController = Class.create({ initialize: function(domNode) { // root node for the navigator control this.domNode = domNode; // initialize an instance of NavigatorThumbnailSidebar object this.thumbnailSidebar = new NavigatorThumbnailSidebar(); // initialize an instance of NavigatorThumbnailScroller object this.thumbnailScroller = new NavigatorThumbnailScroller(); // initialize an instance of NavigatorThumbnailSelection object this.thumbnailSelection = new NavigatorThumbnailSelection(); // initialize an instance of NavigatorThumbnailContainer object this.thumbnailContainer = new NavigatorThumbnailContainer(); this.thumbnailSidebar.domNode.appendChild(this.thumbnailScroller.domNode); this.thumbnailScroller.domNode.appendChild(this.thumbnailSelection.domNode); this.thumbnailScroller.domNode.appendChild(this.thumbnailContainer.domNode); this.domNode.appendChild(this.thumbnailSidebar.domNode); // create left sidebar to react to events this.leftSidebar = new NavigatorLeftSidebar(); this.domNode.appendChild(this.leftSidebar.domNode); // mouse events Event.observe(this.domNode, "click", this.handleClickEvent.bind(this)); Event.observe(this.leftSidebar.domNode, "mouseover", this.handleMouseOverEvent.bind(this)); Event.observe(this.domNode, "mouseleave", this.handleMouseOutEvent.bind(this)); // events document.observe(kSlideIndexDidChangeEvent, this.handleSlideIndexDidChangeEvent.bind(this)); document.observe(kScriptDidDownloadEvent, this.handleScriptDidDownloadEvent.bind(this)); this.slideThumbnail = null; }, initScrollbar: function(){ if (this.thumbnailScroller.domNode.scrollHeight > this.thumbnailScroller.domNode.offsetHeight) { this.thumbnailScroller.domNode.style.width = "126px"; } else { this.thumbnailScroller.domNode.style.width = "129px"; } // adjust navigator width for IE // see IE9/10: Navigator scroll bar touching slide thumbnails while in show mode if (browserPrefix === "ms") { this.domNode.style.width = "148px"; this.thumbnailSidebar.domNode.style.left = "-148px"; this.thumbnailSidebar.domNode.style.width = "137px"; this.thumbnailScroller.domNode.style.width = "137px"; } }, handleClickEvent: function(event) { if (gShowController.isRecording) { return; } event = event || window.event; var target = event.target || event.srcElement; var slideNumber; // stop event from propagating up if (browserPrefix === "ms") { event.cancelBubble = true; } else { event.stopPropagation(); } while ((target.slideNumber == null) && target.nodeName.toLowerCase() != 'body') { target = target.parentNode; } if (target.slideNumber) { this.selectedSlideIndex = target.slideNumber; this.select(this.selectedSlideIndex); } }, select: function(slideIndex) { gShowController.jumpToSlide(slideIndex); }, handleMouseOverEvent: function(event) { event = event || window.event; // do not show navigator when the show is starting var x = 0; var y = 0; if (event.pageX || event.pageY) { x = event.pageX; y = event.pageY; } else if (event.clientX || event.clientY) { x = event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft; y = event.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop; } if (x === 0 && y === 0) { return; } // calculate thumbnailScroller scrollTop position var position = this.selectedSlideIndex * 76; var scrollTop = this.thumbnailScroller.domNode.scrollTop; var height = this.thumbnailScroller.domNode.clientHeight; if (scrollTop > position) { this.thumbnailScroller.domNode.scrollTop = position; } else if (scrollTop + height < position + 76) { var minimumScrollingAmount = position - scrollTop - height + 76; this.thumbnailScroller.domNode.scrollTop = this.thumbnailScroller.domNode.scrollTop + minimumScrollingAmount; } clearTimeout(this.navigatorTimeout); this.navigatorTimeout = setTimeout(this.thumbnailSidebar.show.bind(this.thumbnailSidebar, this.leftSidebar), 400); }, handleMouseOutEvent: function(event) { clearTimeout(this.navigatorTimeout); this.navigatorTimeout = setTimeout(this.thumbnailSidebar.hide.bind(this.thumbnailSidebar, this.leftSidebar), 400); }, handleSlideIndexDidChangeEvent: function(event) { this.selectedSlideIndex = event.memo.slideIndex; this.thumbnailSelection.select(this.selectedSlideIndex); }, handleScriptDidDownloadEvent: function(event) { var script = event.memo.script; for (var i = 0, length = script.slideList.length; i < length; i++) { var slideId = script.slideList[i]; // initialize thumbnail item var thumbnailItem = new NavigatorThumbnailItem(); thumbnailItem.domNode.slideNumber = i + 1; thumbnailItem.numberNode.innerHTML = i + 1; setElementProperty(thumbnailItem.domNode, "top", i * 76 + "px"); this.thumbnailContainer.addItem(thumbnailItem); // do not request thumbnails when delegate will be providing if (gShowController.delegate.getKPFJsonStringForShow == null) { // create slide img object var src = "../" + slideId + "/thumbnail.jpeg"; var img = document.createElement("img"); Event.observe(img, "load", this.updateThumbnail.bind(this, i, img)); img.src = src; } else { gShowController.delegate.loadTextureBySlideIndex( i, { "type": "slideThumbnail", "state":"outgoing" }, (function (slideIndex, domNode){ this.updateThumbnail(slideIndex, domNode); }).bind(this, i)); } } this.initScrollbar(); }, updateThumbnail: function(slideIndex, domNode){ var canvasContainer = this.thumbnailContainer.thumbnailItems[slideIndex].canvasContainer; if (this.slideThumbnail == null) { var originalSlideWidth = gShowController.script.originalSlideWidth; var originalSlideHeight = gShowController.script.originalSlideHeight; var aspectRatio = originalSlideWidth / originalSlideHeight; var width, height; if (aspectRatio >= 4/3) { width = 88; height = Math.ceil(88 * (1/aspectRatio)); } else { width = Math.ceil(66 * aspectRatio); height = 66; } this.slideThumbnail = { width: width, height: height, top: Math.ceil((66 - height)/2), left: Math.ceil((88 - width)/2), scaleX: width / originalSlideWidth, scaleY: height / originalSlideHeight } } if (domNode.nodeName.toLowerCase() === "svg") { domNode.firstElementChild.setAttribute("transform", "matrix(" + this.slideThumbnail.scaleX + ",0,0," + this.slideThumbnail.scaleY + ",0,0)"); } domNode.setAttribute("style", kTransitionPropertyName + ":opacity; " + kTransitionDurationName + ":500; width:" + this.slideThumbnail.width + "px; height:" + this.slideThumbnail.height + "px; left:" + this.slideThumbnail.left + "px; top:" + this.slideThumbnail.top + "px; opacity: 0; position: absolute;"); // prevent the thumbnail from being dragged domNode.setAttribute("draggable", false); if (browserPrefix === "moz") { Event.observe(domNode, "dragstart", function(e){e.preventDefault();}); } canvasContainer.appendChild(domNode); domNode.style.opacity = 1; } }); var NavigatorLeftSidebar = Class.create({ initialize: function() { this.domNode = document.createElement("div"); this.domNode.setAttribute("class", "navigatorLeftSidebar"); } }); var NavigatorThumbnailSidebar = Class.create({ initialize: function() { // root node for the sidebar this.domNode = document.createElement("div"); this.domNode.setAttribute("class", "navigatorThumbnailSidebar"); }, show: function(leftSidebar) { leftSidebar.domNode.style.visibility = "hidden"; this.domNode.style.left = "0px"; gShowController.displayManager.navigatorIsShowing = true; gShowController.displayManager.clearTimeoutForCursor(); }, hide: function(leftSidebar) { leftSidebar.domNode.style.visibility = "visible"; this.domNode.style.left = "-140px"; gShowController.displayManager.navigatorIsShowing = false; gShowController.displayManager.setTimeoutForCursor(); } }); var NavigatorThumbnailScroller = Class.create({ initialize: function() { // root node for the scroller this.domNode = document.createElement("div"); this.domNode.setAttribute("class", "navigatorThumbnailScroller"); } }); var NavigatorThumbnailSelection = Class.create({ initialize: function(params) { this.domNode = document.createElement("div"); this.domNode.setAttribute("class", "navigatorThumbnailSelection"); }, select: function(slideIndex) { this.domNode.style.top = 76 * slideIndex + "px"; this.domNode.style.display = "block"; } }); var NavigatorThumbnailContainer = Class.create({ initialize: function() { // thumbnail container domNode this.domNode = document.createElement("div"); this.domNode.setAttribute("class", "navigatorThumbnailContainer"); // item container this.thumbnailItems = []; }, addItem: function(thumbnailItem) { this.thumbnailItems.push(thumbnailItem); this.domNode.appendChild(thumbnailItem.domNode); } }); var NavigatorThumbnailItem = Class.create({ initialize: function() { // thumbnail item root this.domNode = document.createElement("div"); this.domNode.setAttribute("class", "navigatorThumbnailItem"); // thumbnail content node this.thumbnailContentNode = document.createElement("div"); this.thumbnailContentNode.setAttribute("style", "position: absolute; height: 76px; width: 119px;"); // number node this.numberNode = document.createElement("div"); this.numberNode.setAttribute("style", "position: absolute; bottom: 1px; width: 20px; height: 20px; text-align: right; font-weight: bold; color: white;"); // image node this.imageNode = document.createElement("div"); this.imageNode.setAttribute("style", "position: absolute; left: 24px; width: 95px; height: 76px;"); // thumb node this.thumb = document.createElement("div"); this.thumb.setAttribute("style", "position: absolute; top: 4px; width: 90px; height: 68px;"); // create canvas container object this.canvasContainer = document.createElement("div"); this.canvasContainer.setAttribute("class", "navigatorThumbnailItemCanvasContainer"); // add thumbnail image this.thumb.appendChild(this.canvasContainer); this.imageNode.appendChild(this.thumb); this.thumbnailContentNode.appendChild(this.numberNode); this.thumbnailContentNode.appendChild(this.imageNode); this.domNode.appendChild(this.thumbnailContentNode); } });