You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
12KB

  1. /*
  2. * NavigatorController.js
  3. * Keynote HTML Player
  4. *
  5. * Created by Tungwei Cheng
  6. * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
  7. */
  8. var NavigatorController = Class.create({
  9. initialize: function(domNode) {
  10. // root node for the navigator control
  11. this.domNode = domNode;
  12. // initialize an instance of NavigatorThumbnailSidebar object
  13. this.thumbnailSidebar = new NavigatorThumbnailSidebar();
  14. // initialize an instance of NavigatorThumbnailScroller object
  15. this.thumbnailScroller = new NavigatorThumbnailScroller();
  16. // initialize an instance of NavigatorThumbnailSelection object
  17. this.thumbnailSelection = new NavigatorThumbnailSelection();
  18. // initialize an instance of NavigatorThumbnailContainer object
  19. this.thumbnailContainer = new NavigatorThumbnailContainer();
  20. this.thumbnailSidebar.domNode.appendChild(this.thumbnailScroller.domNode);
  21. this.thumbnailScroller.domNode.appendChild(this.thumbnailSelection.domNode);
  22. this.thumbnailScroller.domNode.appendChild(this.thumbnailContainer.domNode);
  23. this.domNode.appendChild(this.thumbnailSidebar.domNode);
  24. // create left sidebar to react to events
  25. this.leftSidebar = new NavigatorLeftSidebar();
  26. this.domNode.appendChild(this.leftSidebar.domNode);
  27. // mouse events
  28. Event.observe(this.domNode, "click", this.handleClickEvent.bind(this));
  29. Event.observe(this.leftSidebar.domNode, "mouseover", this.handleMouseOverEvent.bind(this));
  30. Event.observe(this.domNode, "mouseleave", this.handleMouseOutEvent.bind(this));
  31. // events
  32. document.observe(kSlideIndexDidChangeEvent, this.handleSlideIndexDidChangeEvent.bind(this));
  33. document.observe(kScriptDidDownloadEvent, this.handleScriptDidDownloadEvent.bind(this));
  34. this.slideThumbnail = null;
  35. },
  36. initScrollbar: function(){
  37. if (this.thumbnailScroller.domNode.scrollHeight > this.thumbnailScroller.domNode.offsetHeight) {
  38. this.thumbnailScroller.domNode.style.width = "126px";
  39. } else {
  40. this.thumbnailScroller.domNode.style.width = "129px";
  41. }
  42. // adjust navigator width for IE
  43. // see <rdar://problem/12511461> IE9/10: Navigator scroll bar touching slide thumbnails while in show mode
  44. if (browserPrefix === "ms") {
  45. this.domNode.style.width = "148px";
  46. this.thumbnailSidebar.domNode.style.left = "-148px";
  47. this.thumbnailSidebar.domNode.style.width = "137px";
  48. this.thumbnailScroller.domNode.style.width = "137px";
  49. }
  50. },
  51. handleClickEvent: function(event) {
  52. if (gShowController.isRecording) {
  53. return;
  54. }
  55. event = event || window.event;
  56. var target = event.target || event.srcElement;
  57. var slideNumber;
  58. // stop event from propagating up
  59. if (browserPrefix === "ms") {
  60. event.cancelBubble = true;
  61. } else {
  62. event.stopPropagation();
  63. }
  64. while ((target.slideNumber == null) && target.nodeName.toLowerCase() != 'body') {
  65. target = target.parentNode;
  66. }
  67. if (target.slideNumber) {
  68. this.selectedSlideIndex = target.slideNumber;
  69. this.select(this.selectedSlideIndex);
  70. }
  71. },
  72. select: function(slideIndex) {
  73. gShowController.jumpToSlide(slideIndex);
  74. },
  75. handleMouseOverEvent: function(event) {
  76. event = event || window.event;
  77. // do not show navigator when the show is starting
  78. var x = 0;
  79. var y = 0;
  80. if (event.pageX || event.pageY) {
  81. x = event.pageX;
  82. y = event.pageY;
  83. } else if (event.clientX || event.clientY) {
  84. x = event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
  85. y = event.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop;
  86. }
  87. if (x === 0 && y === 0) {
  88. return;
  89. }
  90. // calculate thumbnailScroller scrollTop position
  91. var position = this.selectedSlideIndex * 76;
  92. var scrollTop = this.thumbnailScroller.domNode.scrollTop;
  93. var height = this.thumbnailScroller.domNode.clientHeight;
  94. if (scrollTop > position) {
  95. this.thumbnailScroller.domNode.scrollTop = position;
  96. } else if (scrollTop + height < position + 76) {
  97. var minimumScrollingAmount = position - scrollTop - height + 76;
  98. this.thumbnailScroller.domNode.scrollTop = this.thumbnailScroller.domNode.scrollTop + minimumScrollingAmount;
  99. }
  100. clearTimeout(this.navigatorTimeout);
  101. this.navigatorTimeout = setTimeout(this.thumbnailSidebar.show.bind(this.thumbnailSidebar, this.leftSidebar), 400);
  102. },
  103. handleMouseOutEvent: function(event) {
  104. clearTimeout(this.navigatorTimeout);
  105. this.navigatorTimeout = setTimeout(this.thumbnailSidebar.hide.bind(this.thumbnailSidebar, this.leftSidebar), 400);
  106. },
  107. handleSlideIndexDidChangeEvent: function(event) {
  108. this.selectedSlideIndex = event.memo.slideIndex;
  109. this.thumbnailSelection.select(this.selectedSlideIndex);
  110. },
  111. handleScriptDidDownloadEvent: function(event) {
  112. var script = event.memo.script;
  113. for (var i = 0, length = script.slideList.length; i < length; i++) {
  114. var slideId = script.slideList[i];
  115. // initialize thumbnail item
  116. var thumbnailItem = new NavigatorThumbnailItem();
  117. thumbnailItem.domNode.slideNumber = i + 1;
  118. thumbnailItem.numberNode.innerHTML = i + 1;
  119. setElementProperty(thumbnailItem.domNode, "top", i * 76 + "px");
  120. this.thumbnailContainer.addItem(thumbnailItem);
  121. // do not request thumbnails when delegate will be providing
  122. if (gShowController.delegate.getKPFJsonStringForShow == null) {
  123. // create slide img object
  124. var src = "../" + slideId + "/thumbnail.jpeg";
  125. var img = document.createElement("img");
  126. Event.observe(img, "load", this.updateThumbnail.bind(this, i, img));
  127. img.src = src;
  128. } else {
  129. gShowController.delegate.loadTextureBySlideIndex(
  130. i,
  131. {
  132. "type": "slideThumbnail",
  133. "state":"outgoing"
  134. },
  135. (function (slideIndex, domNode){
  136. this.updateThumbnail(slideIndex, domNode);
  137. }).bind(this, i));
  138. }
  139. }
  140. this.initScrollbar();
  141. },
  142. updateThumbnail: function(slideIndex, domNode){
  143. var canvasContainer = this.thumbnailContainer.thumbnailItems[slideIndex].canvasContainer;
  144. if (this.slideThumbnail == null) {
  145. var originalSlideWidth = gShowController.script.originalSlideWidth;
  146. var originalSlideHeight = gShowController.script.originalSlideHeight;
  147. var aspectRatio = originalSlideWidth / originalSlideHeight;
  148. var width, height;
  149. if (aspectRatio >= 4/3) {
  150. width = 88;
  151. height = Math.ceil(88 * (1/aspectRatio));
  152. } else {
  153. width = Math.ceil(66 * aspectRatio);
  154. height = 66;
  155. }
  156. this.slideThumbnail = {
  157. width: width,
  158. height: height,
  159. top: Math.ceil((66 - height)/2),
  160. left: Math.ceil((88 - width)/2),
  161. scaleX: width / originalSlideWidth,
  162. scaleY: height / originalSlideHeight
  163. }
  164. }
  165. if (domNode.nodeName.toLowerCase() === "svg") {
  166. domNode.firstElementChild.setAttribute("transform", "matrix(" + this.slideThumbnail.scaleX + ",0,0," + this.slideThumbnail.scaleY + ",0,0)");
  167. }
  168. 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;");
  169. // prevent the thumbnail from being dragged
  170. domNode.setAttribute("draggable", false);
  171. if (browserPrefix === "moz") {
  172. Event.observe(domNode, "dragstart", function(e){e.preventDefault();});
  173. }
  174. canvasContainer.appendChild(domNode);
  175. domNode.style.opacity = 1;
  176. }
  177. });
  178. var NavigatorLeftSidebar = Class.create({
  179. initialize: function() {
  180. this.domNode = document.createElement("div");
  181. this.domNode.setAttribute("class", "navigatorLeftSidebar");
  182. }
  183. });
  184. var NavigatorThumbnailSidebar = Class.create({
  185. initialize: function() {
  186. // root node for the sidebar
  187. this.domNode = document.createElement("div");
  188. this.domNode.setAttribute("class", "navigatorThumbnailSidebar");
  189. },
  190. show: function(leftSidebar) {
  191. leftSidebar.domNode.style.visibility = "hidden";
  192. this.domNode.style.left = "0px";
  193. gShowController.displayManager.navigatorIsShowing = true;
  194. gShowController.displayManager.clearTimeoutForCursor();
  195. },
  196. hide: function(leftSidebar) {
  197. leftSidebar.domNode.style.visibility = "visible";
  198. this.domNode.style.left = "-140px";
  199. gShowController.displayManager.navigatorIsShowing = false;
  200. gShowController.displayManager.setTimeoutForCursor();
  201. }
  202. });
  203. var NavigatorThumbnailScroller = Class.create({
  204. initialize: function() {
  205. // root node for the scroller
  206. this.domNode = document.createElement("div");
  207. this.domNode.setAttribute("class", "navigatorThumbnailScroller");
  208. }
  209. });
  210. var NavigatorThumbnailSelection = Class.create({
  211. initialize: function(params) {
  212. this.domNode = document.createElement("div");
  213. this.domNode.setAttribute("class", "navigatorThumbnailSelection");
  214. },
  215. select: function(slideIndex) {
  216. this.domNode.style.top = 76 * slideIndex + "px";
  217. this.domNode.style.display = "block";
  218. }
  219. });
  220. var NavigatorThumbnailContainer = Class.create({
  221. initialize: function() {
  222. // thumbnail container domNode
  223. this.domNode = document.createElement("div");
  224. this.domNode.setAttribute("class", "navigatorThumbnailContainer");
  225. // item container
  226. this.thumbnailItems = [];
  227. },
  228. addItem: function(thumbnailItem) {
  229. this.thumbnailItems.push(thumbnailItem);
  230. this.domNode.appendChild(thumbnailItem.domNode);
  231. }
  232. });
  233. var NavigatorThumbnailItem = Class.create({
  234. initialize: function() {
  235. // thumbnail item root
  236. this.domNode = document.createElement("div");
  237. this.domNode.setAttribute("class", "navigatorThumbnailItem");
  238. // thumbnail content node
  239. this.thumbnailContentNode = document.createElement("div");
  240. this.thumbnailContentNode.setAttribute("style", "position: absolute; height: 76px; width: 119px;");
  241. // number node
  242. this.numberNode = document.createElement("div");
  243. this.numberNode.setAttribute("style", "position: absolute; bottom: 1px; width: 20px; height: 20px; text-align: right; font-weight: bold; color: white;");
  244. // image node
  245. this.imageNode = document.createElement("div");
  246. this.imageNode.setAttribute("style", "position: absolute; left: 24px; width: 95px; height: 76px;");
  247. // thumb node
  248. this.thumb = document.createElement("div");
  249. this.thumb.setAttribute("style", "position: absolute; top: 4px; width: 90px; height: 68px;");
  250. // create canvas container object
  251. this.canvasContainer = document.createElement("div");
  252. this.canvasContainer.setAttribute("class", "navigatorThumbnailItemCanvasContainer");
  253. // add thumbnail image
  254. this.thumb.appendChild(this.canvasContainer);
  255. this.imageNode.appendChild(this.thumb);
  256. this.thumbnailContentNode.appendChild(this.numberNode);
  257. this.thumbnailContentNode.appendChild(this.imageNode);
  258. this.domNode.appendChild(this.thumbnailContentNode);
  259. }
  260. });