JJ Allaire | 064552c | 2017-02-10 10:28:41 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Reveal.js menu plugin |
| 3 | * MIT licensed |
| 4 | * (c) Greg Denehy 2015 |
| 5 | */ |
| 6 | |
| 7 | var RevealMenu = window.RevealMenu || (function(){ |
| 8 | var config = Reveal.getConfig(); |
| 9 | var options = config.menu || {}; |
| 10 | options.path = options.path || scriptPath() || 'plugin/menu'; |
| 11 | |
| 12 | var module = {}; |
| 13 | |
| 14 | loadResource(options.path + '/lib/jeesh.min.js', 'script', function() { |
| 15 | loadResource(options.path + '/lib/bowser.min.js', 'script', function() { |
| 16 | loadResource(options.path + '/menu.css', 'stylesheet', function() { |
| 17 | // does not support IE8 or below |
| 18 | if (!bowser.msie || bowser.version >= 9) { |
| 19 | // |
| 20 | // Set option defaults |
| 21 | // |
| 22 | var side = options.side || 'left'; // 'left' or 'right' |
| 23 | var numbers = options.numbers || false; |
| 24 | var titleSelector = 'h1, h2, h3, h4, h5'; |
| 25 | if (typeof options.titleSelector === 'string') titleSelector = options.titleSelector; |
| 26 | var hideMissingTitles = options.hideMissingTitles || false; |
| 27 | var markers = options.markers || false; |
| 28 | var custom = options.custom; |
| 29 | var themes = options.themes; |
| 30 | if (typeof themes === "undefined") { |
| 31 | themes = [ |
| 32 | { name: 'Black', theme: 'css/theme/black.css' }, |
| 33 | { name: 'White', theme: 'css/theme/white.css' }, |
| 34 | { name: 'League', theme: 'css/theme/league.css' }, |
| 35 | { name: 'Sky', theme: 'css/theme/sky.css' }, |
| 36 | { name: 'Beige', theme: 'css/theme/beige.css' }, |
| 37 | { name: 'Simple', theme: 'css/theme/simple.css' }, |
| 38 | { name: 'Serif', theme: 'css/theme/serif.css' }, |
| 39 | { name: 'Blood', theme: 'css/theme/blood.css' }, |
| 40 | { name: 'Night', theme: 'css/theme/night.css' }, |
| 41 | { name: 'Moon', theme: 'css/theme/moon.css' }, |
| 42 | { name: 'Solarized', theme: 'css/theme/solarized.css' } |
| 43 | ]; |
| 44 | } |
| 45 | var transitions = options.transitions; |
| 46 | if (typeof transitions === "undefined") transitions = true; |
| 47 | if (bowser.msie && bowser.version <= 9) { |
| 48 | // transitions aren't support in IE9 anyway, so no point in showing them |
| 49 | transitions = false; |
| 50 | } |
| 51 | var openButton = options.openButton; |
| 52 | if (typeof openButton === "undefined") openButton = true; |
| 53 | var openSlideNumber = options.openSlideNumber; |
| 54 | if (typeof openSlideNumber === "undefined") openSlideNumber = false; |
| 55 | var keyboard = options.keyboard; |
| 56 | if (typeof keyboard === "undefined") keyboard = true; |
| 57 | |
| 58 | function disableMouseSelection() { |
| 59 | mouseSelectionEnabled = false; |
| 60 | } |
| 61 | |
| 62 | function reenableMouseSelection() { |
| 63 | // wait until the mouse has moved before re-enabling mouse selection |
| 64 | // to avoid selections on scroll |
| 65 | $('nav.slide-menu').one('mousemove', function(event) { |
| 66 | //XXX this should select the item under the mouse |
| 67 | mouseSelectionEnabled = true; |
| 68 | }); |
| 69 | } |
| 70 | |
| 71 | // |
| 72 | // Keyboard handling |
| 73 | // |
| 74 | function getOffset(el) { |
| 75 | var _x = 0; |
| 76 | var _y = 0; |
| 77 | while(el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) { |
| 78 | _x += el.offsetLeft - el.scrollLeft; |
| 79 | _y += el.offsetTop - el.scrollTop; |
| 80 | el = el.offsetParent; |
| 81 | } |
| 82 | return { top: _y, left: _x }; |
| 83 | } |
| 84 | |
| 85 | function visibleOffset(el) { |
| 86 | var offsetFromTop = getOffset(el).top - el.offsetParent.offsetTop; |
| 87 | if (offsetFromTop < 0) return -offsetFromTop |
| 88 | var offsetFromBottom = el.offsetParent.offsetHeight - (el.offsetTop - el.offsetParent.scrollTop + el.offsetHeight); |
| 89 | if (offsetFromBottom < 0) return offsetFromBottom; |
| 90 | return 0; |
| 91 | } |
| 92 | |
| 93 | function keepVisible(el) { |
| 94 | var offset = visibleOffset(el); |
| 95 | if (offset) { |
| 96 | disableMouseSelection(); |
| 97 | el.scrollIntoView(offset > 0); |
| 98 | reenableMouseSelection(); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | function scrollItemToTop(el) { |
| 103 | disableMouseSelection(); |
| 104 | el.offsetParent.scrollTop = el.offsetTop; |
| 105 | reenableMouseSelection(); |
| 106 | } |
| 107 | |
| 108 | function scrollItemToBottom(el) { |
| 109 | disableMouseSelection(); |
| 110 | el.offsetParent.scrollTop = el.offsetTop - el.offsetParent.offsetHeight + el.offsetHeight |
| 111 | reenableMouseSelection(); |
| 112 | } |
| 113 | |
| 114 | function selectItem(el) { |
| 115 | $(el).addClass('selected'); |
| 116 | keepVisible(el); |
| 117 | } |
| 118 | |
| 119 | function onDocumentKeyDown(event) { |
| 120 | if (event.keyCode === 77) { |
| 121 | toggleMenu(); |
| 122 | } else if (isOpen()) { |
| 123 | event.stopImmediatePropagation(); |
| 124 | switch( event.keyCode ) { |
| 125 | // h, left - change panel |
| 126 | case 72: case 37: |
| 127 | prevPanel(); |
| 128 | break; |
| 129 | // l, right - change panel |
| 130 | case 76: case 39: |
| 131 | nextPanel(); |
| 132 | break; |
| 133 | // k, up |
| 134 | case 75: case 38: |
| 135 | var currItem = $('.active-menu-panel .slide-menu-items li.selected').get(0) || $('.active-menu-panel .slide-menu-items li.active').get(0); |
| 136 | if (currItem) { |
| 137 | $('.active-menu-panel .slide-menu-items li').removeClass('selected'); |
| 138 | var nextItem = $('.active-menu-panel .slide-menu-items li[data-item="' + ($(currItem).data('item') - 1) + '"]').get(0) || currItem; |
| 139 | selectItem(nextItem); |
| 140 | } else { |
| 141 | var items = $('.active-menu-panel .slide-menu-items li.slide-menu-item'); |
| 142 | if (items.length > 0) { |
| 143 | selectItem(items.get(0)); |
| 144 | } |
| 145 | } |
| 146 | break; |
| 147 | // j, down |
| 148 | case 74: case 40: |
| 149 | var currItem = $('.active-menu-panel .slide-menu-items li.selected').get(0) || $('.active-menu-panel .slide-menu-items li.active').get(0); |
| 150 | if (currItem) { |
| 151 | $('.active-menu-panel .slide-menu-items li').removeClass('selected'); |
| 152 | var nextItem = $('.active-menu-panel .slide-menu-items li[data-item="' + ($(currItem).data('item') + 1) + '"]').get(0) || currItem; |
| 153 | selectItem(nextItem); |
| 154 | } else { |
| 155 | var items = $('.active-menu-panel .slide-menu-items li.slide-menu-item'); |
| 156 | if (items.length > 0) { |
| 157 | selectItem(items.get(0)); |
| 158 | } |
| 159 | } |
| 160 | break; |
| 161 | // pageup, u |
| 162 | case 33: case 85: |
| 163 | var itemsAbove = $('.active-menu-panel .slide-menu-items li').filter(function(item) { return visibleOffset(item) > 0; }); |
| 164 | var visibleItems = $('.active-menu-panel .slide-menu-items li').filter(function(item) { return visibleOffset(item) == 0; }); |
| 165 | |
| 166 | var firstVisible = (itemsAbove.length > 0 && Math.abs(visibleOffset(itemsAbove[itemsAbove.length-1])) < itemsAbove[itemsAbove.length-1].clientHeight ? itemsAbove[itemsAbove.length-1] : visibleItems[0]); |
| 167 | if (firstVisible) { |
| 168 | if ($(firstVisible).hasClass('selected') && itemsAbove.length > 0) { |
| 169 | // at top of viewport already, page scroll (if not at start) |
| 170 | // ...move selected item to bottom, and change selection to last fully visible item at top |
| 171 | scrollItemToBottom(firstVisible); |
| 172 | visibleItems = $('.active-menu-panel .slide-menu-items li').filter(function(item) { return visibleOffset(item) == 0; }); |
| 173 | if (visibleItems[0] == firstVisible) { |
| 174 | // prev item is still beyond the viewport (for custom panels) |
| 175 | firstVisible = itemsAbove[itemsAbove.length-1]; |
| 176 | } else { |
| 177 | firstVisible = visibleItems[0]; |
| 178 | } |
| 179 | } |
| 180 | $('.active-menu-panel .slide-menu-items li').removeClass('selected'); |
| 181 | selectItem(firstVisible); |
| 182 | // ensure selected item is positioned at the top of the viewport |
| 183 | scrollItemToTop(firstVisible); |
| 184 | } |
| 185 | break; |
| 186 | // pagedown, d |
| 187 | case 34: case 68: |
| 188 | var visibleItems = $('.active-menu-panel .slide-menu-items li').filter(function(item) { return visibleOffset(item) == 0; }); |
| 189 | var itemsBelow = $('.active-menu-panel .slide-menu-items li').filter(function(item) { return visibleOffset(item) < 0; }); |
| 190 | |
| 191 | var lastVisible = (itemsBelow.length > 0 && Math.abs(visibleOffset(itemsBelow[0])) < itemsBelow[0].clientHeight ? itemsBelow[0] : visibleItems[visibleItems.length-1]); |
| 192 | if (lastVisible) { |
| 193 | if ($(lastVisible).hasClass('selected') && itemsBelow.length > 0) { |
| 194 | // at bottom of viewport already, page scroll (if not at end) |
| 195 | // ...move selected item to top, and change selection to last fully visible item at bottom |
| 196 | scrollItemToTop(lastVisible); |
| 197 | visibleItems = $('.active-menu-panel .slide-menu-items li').filter(function(item) { return visibleOffset(item) == 0; }); |
| 198 | if (visibleItems[visibleItems.length-1] == lastVisible) { |
| 199 | // next item is still beyond the viewport (for custom panels) |
| 200 | lastVisible = itemsBelow[0]; |
| 201 | } else { |
| 202 | lastVisible = visibleItems[visibleItems.length-1]; |
| 203 | } |
| 204 | } |
| 205 | $('.active-menu-panel .slide-menu-items li').removeClass('selected'); |
| 206 | selectItem(lastVisible); |
| 207 | // ensure selected item is positioned at the bottom of the viewport |
| 208 | scrollItemToBottom(lastVisible); |
| 209 | } |
| 210 | break; |
| 211 | // home |
| 212 | case 36: |
| 213 | $('.active-menu-panel .slide-menu-items li').removeClass('selected'); |
| 214 | var sel = $('.active-menu-panel .slide-menu-items li:first-of-type'); |
| 215 | if (sel.length > 0) { |
| 216 | keepVisible(sel.addClass('selected').get(0)); |
| 217 | } |
| 218 | break; |
| 219 | // end |
| 220 | case 35: |
| 221 | $('.active-menu-panel .slide-menu-items li').removeClass('selected'); |
| 222 | var sel = $('.active-menu-panel .slide-menu-items li:last-of-type'); |
| 223 | if (sel.length > 0) { |
| 224 | keepVisible(sel.addClass('selected').get(0)); |
| 225 | } |
| 226 | break; |
| 227 | // space, return |
| 228 | case 32: case 13: |
| 229 | var currItem = $('.active-menu-panel .slide-menu-items li.selected').get(0); |
| 230 | if (currItem) { |
| 231 | openItem(currItem); |
| 232 | } |
| 233 | break; |
| 234 | // esc |
| 235 | case 27: closeMenu(); break; |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | if (keyboard) { |
| 241 | //XXX add keyboard option for custom key codes, etc. |
| 242 | |
| 243 | document.addEventListener('keydown', onDocumentKeyDown, false); |
| 244 | |
| 245 | // handle key presses within speaker notes |
| 246 | window.addEventListener( 'message', function( event ) { |
| 247 | var data = JSON.parse( event.data ); |
| 248 | if (data.method === 'triggerKey') { |
| 249 | onDocumentKeyDown( { keyCode: data.args[0], stopImmediatePropagation: function() {} } ); |
| 250 | } |
| 251 | }); |
| 252 | |
| 253 | // Prevent reveal from processing keyboard events when the menu is open |
| 254 | if (config.keyboardCondition && typeof config.keyboardCondition === 'function') { |
| 255 | // combine user defined keyboard condition with the menu's own condition |
| 256 | var userCondition = config.keyboardCondition; |
| 257 | config.keyboardCondition = function() { |
| 258 | return userCondition() && !isOpen(); |
| 259 | }; |
| 260 | } else { |
| 261 | config.keyboardCondition = function() { return !isOpen(); } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | |
| 266 | // |
| 267 | // Utilty functions |
| 268 | // |
| 269 | |
| 270 | function openMenu(event) { |
| 271 | if (event) event.preventDefault(); |
| 272 | if (!isOpen()) { |
| 273 | $('body').addClass('slide-menu-active'); |
| 274 | $('.reveal').addClass('has-' + options.effect + '-' + side); |
| 275 | $('.slide-menu').addClass('active'); |
| 276 | $('.slide-menu-overlay').addClass('active'); |
| 277 | |
| 278 | // identify active theme |
| 279 | $('div[data-panel="Themes"] li').removeClass('active'); |
| 280 | $('li[data-theme="' + $('#theme').attr('href') + '"]').addClass('active'); |
| 281 | |
| 282 | // identify active transition |
| 283 | $('div[data-panel="Transitions"] li').removeClass('active'); |
| 284 | $('li[data-transition="' + Reveal.getConfig().transition + '"]').addClass('active'); |
| 285 | |
| 286 | // set item selections to match active items |
| 287 | $('.slide-menu-panel li.active') |
| 288 | .addClass('selected') |
| 289 | .each(function(item) { keepVisible(item) }); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | function closeMenu(event) { |
| 294 | if (event) event.preventDefault(); |
| 295 | $('body').removeClass('slide-menu-active'); |
| 296 | $('.reveal').removeClass('has-' + options.effect + '-' + side); |
| 297 | $('.slide-menu').removeClass('active'); |
| 298 | $('.slide-menu-overlay').removeClass('active'); |
| 299 | $('.slide-menu-panel li.selected').removeClass('selected'); |
| 300 | } |
| 301 | |
| 302 | function toggleMenu(event) { |
| 303 | if (isOpen()) { |
| 304 | closeMenu(event); |
| 305 | } else { |
| 306 | openMenu(event); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | function isOpen() { |
| 311 | return $('body').hasClass('slide-menu-active'); |
| 312 | } |
| 313 | |
| 314 | function openPanel(e) { |
| 315 | openMenu(); |
| 316 | var panel = e; |
| 317 | if (typeof e !== "string") { |
| 318 | panel = $(e.currentTarget).data('panel'); |
| 319 | } |
| 320 | $('.slide-menu-toolbar > li').removeClass('active-toolbar-button'); |
| 321 | $('li[data-panel="' + panel + '"]').addClass('active-toolbar-button'); |
| 322 | $('.slide-menu-panel').removeClass('active-menu-panel'); |
| 323 | $('div[data-panel="' + panel + '"]').addClass('active-menu-panel'); |
| 324 | } |
| 325 | |
| 326 | function nextPanel() { |
| 327 | var next = ($('.active-toolbar-button').data('button') + 1) % buttons; |
| 328 | openPanel($('.toolbar-panel-button[data-button="' + next + '"]').data('panel')); |
| 329 | } |
| 330 | |
| 331 | function prevPanel() { |
| 332 | var next = $('.active-toolbar-button').data('button') - 1; |
| 333 | if (next < 0) { |
| 334 | next = buttons - 1; |
| 335 | } |
| 336 | openPanel($('.toolbar-panel-button[data-button="' + next + '"]').data('panel')); |
| 337 | } |
| 338 | |
| 339 | $('<nav class="slide-menu slide-menu--' + side + '"></nav>') |
| 340 | .appendTo($('.reveal')); |
| 341 | $('<div class="slide-menu-overlay"></div>') |
| 342 | .appendTo($('.reveal')) |
| 343 | .click(closeMenu); |
| 344 | |
| 345 | var toolbar = $('<ol class="slide-menu-toolbar"></ol>').prependTo($('.slide-menu')); |
| 346 | var buttons = 0; |
| 347 | $('<li data-panel="Slides" data-button="' + (buttons++) + '" class="toolbar-panel-button"><span class="slide-menu-toolbar-label">Slides</span><br/><i class="fa fa-list"></i></li>') |
| 348 | .appendTo(toolbar) |
| 349 | .addClass('active-toolbar-button') |
| 350 | .click(openPanel); |
| 351 | |
| 352 | if (custom) { |
| 353 | custom.forEach(function(element, index, array) { |
| 354 | $('<li data-panel="Custom' + index + '" data-button="' + (buttons++) + '" class="toolbar-panel-button"><span class="slide-menu-toolbar-label">' + element.title + '</span><br/>' + element.icon + '</i></li>') |
| 355 | .appendTo(toolbar) |
| 356 | .click(openPanel); |
| 357 | }) |
| 358 | } |
| 359 | |
| 360 | if (themes) { |
| 361 | $('<li data-panel="Themes" data-button="' + (buttons++) + '" class="toolbar-panel-button"><span class="slide-menu-toolbar-label">Themes</span><br/><i class="fa fa-desktop"></i></li>') |
| 362 | .appendTo(toolbar) |
| 363 | .click(openPanel); |
| 364 | } |
| 365 | if (transitions) { |
| 366 | $('<li data-panel="Transitions" data-button="' + (buttons++) + '" class="toolbar-panel-button"><span class="slide-menu-toolbar-label">Transitions</span><br/><i class="fa fa-arrows-h"></i></li>') |
| 367 | .appendTo(toolbar) |
| 368 | .click(openPanel); |
| 369 | } |
| 370 | $('<li id="close"><span class="slide-menu-toolbar-label">Close</span><br/><i class="fa fa-times"></i></li>') |
| 371 | .appendTo(toolbar) |
| 372 | .click(closeMenu); |
| 373 | |
| 374 | var panels = $('.slide-menu'); |
| 375 | |
| 376 | // |
| 377 | // Slide links |
| 378 | // |
| 379 | function generateItem(type, section, i, h, v) { |
| 380 | var link = '/#/' + h; |
| 381 | if (typeof v === 'number' && !isNaN( v )) link += '/' + v; |
| 382 | |
| 383 | var title = $(section).data('menu-title') || |
| 384 | $('.menu-title', section).text() || |
| 385 | $(titleSelector, section).text(); |
| 386 | if (!title) { |
| 387 | if (hideMissingTitles) return ''; |
| 388 | title = "Slide " + i; |
| 389 | type += ' no-title'; |
| 390 | } |
| 391 | |
| 392 | title = '<span class="slide-menu-item-title">' + title + '</span>'; |
| 393 | if (numbers) { |
| 394 | // Number formatting taken from reveal.js |
| 395 | var value = []; |
| 396 | var format = 'h.v'; |
| 397 | |
| 398 | // Check if a custom number format is available |
| 399 | if( typeof numbers === 'string' ) { |
| 400 | format = numbers; |
| 401 | } |
| 402 | else if (typeof config.slideNumber === 'string') { |
| 403 | // Take user defined number format for slides |
| 404 | format = config.slideNumber; |
| 405 | } |
| 406 | |
| 407 | switch( format ) { |
| 408 | case 'c': |
| 409 | value.push( i ); |
| 410 | break; |
| 411 | case 'c/t': |
| 412 | value.push( i, '/', Reveal.getTotalSlides() ); |
| 413 | break; |
| 414 | case 'h/v': |
| 415 | value.push( h + 1 ); |
| 416 | if( typeof v === 'number' && !isNaN( v ) ) value.push( '/', v + 1 ); |
| 417 | break; |
| 418 | default: |
| 419 | value.push( h + 1 ); |
| 420 | if( typeof v === 'number' && !isNaN( v ) ) value.push( '.', v + 1 ); |
| 421 | } |
| 422 | |
| 423 | title = '<span class="slide-menu-item-number">' + value.join('') + '. </span>' + title; |
| 424 | } |
| 425 | |
| 426 | var m = ''; |
| 427 | if (markers) { |
| 428 | m = '<i class="fa fa-check-circle past"></i>' + |
| 429 | '<i class="fa fa-dot-circle-o active"></i>' + |
| 430 | '<i class="fa fa-circle-thin future"></i>'; |
| 431 | } |
| 432 | |
| 433 | return '<li class="' + type + '" data-item="' + i + '" data-slide-h="' + h + '" data-slide-v="' + (v === undefined ? 0 : v) + '">' + m + title + '</li>'; |
| 434 | } |
| 435 | |
| 436 | function openItem(item) { |
| 437 | var h = $(item).data('slide-h'); |
| 438 | var v = $(item).data('slide-v'); |
| 439 | var theme = $(item).data('theme'); |
| 440 | var transition = $(item).data('transition'); |
| 441 | if (typeof h !== "undefined" && typeof v !== "undefined") { |
| 442 | Reveal.slide(h, v); |
| 443 | closeMenu(); |
| 444 | } else if (theme) { |
| 445 | $('#theme').attr('href', theme); |
| 446 | closeMenu(); |
| 447 | } else if (transition) { |
| 448 | Reveal.configure({ transition: transition }); |
| 449 | closeMenu(); |
| 450 | } else { |
| 451 | var links = $(item).find('a'); |
| 452 | if (links.length > 0) { |
| 453 | links.get(0).click(); |
| 454 | } |
| 455 | closeMenu(); |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | function clicked(event) { |
| 460 | if (event.target.nodeName !== "A") { |
| 461 | event.preventDefault(); |
| 462 | } |
| 463 | openItem(event.currentTarget); |
| 464 | } |
| 465 | |
| 466 | function highlightCurrentSlide() { |
| 467 | var state = Reveal.getState(); |
| 468 | $('li.slide-menu-item, li.slide-menu-item-vertical') |
| 469 | .removeClass('past') |
| 470 | .removeClass('active') |
| 471 | .removeClass('future'); |
| 472 | |
| 473 | $('li.slide-menu-item, li.slide-menu-item-vertical').each(function(e) { |
| 474 | var h = $(e).data('slide-h'); |
| 475 | var v = $(e).data('slide-v'); |
| 476 | if (h < state.indexh || (h === state.indexh && v < state.indexv)) { |
| 477 | $(e).addClass('past'); |
| 478 | } |
| 479 | else if (h === state.indexh && v === state.indexv) { |
| 480 | $(e).addClass('active'); |
| 481 | } |
| 482 | else { |
| 483 | $(e).addClass('future'); |
| 484 | } |
| 485 | }); |
| 486 | } |
| 487 | |
| 488 | function createSlideMenu() { |
| 489 | if ( !document.querySelector('section[data-markdown]:not([data-markdown-parsed])') ) { |
| 490 | $('<div data-panel="Slides" class="slide-menu-panel"><ul class="slide-menu-items"></ul></div>') |
| 491 | .appendTo(panels) |
| 492 | .addClass('active-menu-panel'); |
| 493 | var items = $('.slide-menu-panel[data-panel="Slides"] > .slide-menu-items'); |
| 494 | var slideCount = 0; |
| 495 | $('.slides > section').each(function(section, h) { |
| 496 | var subsections = $('section', section); |
| 497 | if (subsections.length > 0) { |
| 498 | subsections.each(function(subsection, v) { |
| 499 | var type = (v === 0 ? 'slide-menu-item' : 'slide-menu-item-vertical'); |
| 500 | var item = generateItem(type, subsection, slideCount, h, v); |
| 501 | if (item) { |
| 502 | slideCount++; |
| 503 | items.append(item); |
| 504 | } |
| 505 | }); |
| 506 | } else { |
| 507 | var item = generateItem('slide-menu-item', section, slideCount, h); |
| 508 | if (item) { |
| 509 | slideCount++; |
| 510 | items.append(item); |
| 511 | } |
| 512 | } |
| 513 | }); |
| 514 | $('.slide-menu-item, .slide-menu-item-vertical').click(clicked); |
| 515 | highlightCurrentSlide(); |
| 516 | } |
| 517 | else { |
| 518 | // wait for markdown to be loaded and parsed |
| 519 | setTimeout( createSlideMenu, 100 ); |
| 520 | } |
| 521 | } |
| 522 | |
| 523 | createSlideMenu(); |
| 524 | Reveal.addEventListener('slidechanged', highlightCurrentSlide); |
| 525 | |
| 526 | // |
| 527 | // Custom menu panels |
| 528 | // |
| 529 | if (custom) { |
| 530 | function xhrSuccess () { |
| 531 | if (this.status >= 200 && this.status < 300) { |
| 532 | $(this.responseText).appendTo(this.panel); |
| 533 | enableCustomLinks(this.panel); |
| 534 | } |
| 535 | else { |
| 536 | showErrorMsg(this) |
| 537 | } |
| 538 | } |
| 539 | function xhrError () { |
| 540 | showErrorMsg(this) |
| 541 | } |
| 542 | function loadCustomPanelContent (panel, sURL) { |
| 543 | var oReq = new XMLHttpRequest(); |
| 544 | oReq.panel = panel; |
| 545 | oReq.arguments = Array.prototype.slice.call(arguments, 2); |
| 546 | oReq.onload = xhrSuccess; |
| 547 | oReq.onerror = xhrError; |
| 548 | oReq.open("get", sURL, true); |
| 549 | oReq.send(null); |
| 550 | } |
| 551 | function enableCustomLinks(panel) { |
| 552 | $(panel).find('ul.slide-menu-items li.slide-menu-item').each(function(item, i) { |
| 553 | $(item).attr('data-item', i+1); |
| 554 | $(item).click(clicked); |
| 555 | }); |
| 556 | } |
| 557 | function showErrorMsg(response) { |
| 558 | var msg = '<p>ERROR: The attempt to fetch ' + response.responseURL + ' failed with HTTP status ' + |
| 559 | response.status + ' (' + response.statusText + ').</p>' + |
| 560 | '<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>'; |
| 561 | $(msg).appendTo(response.panel) |
| 562 | } |
| 563 | |
| 564 | custom.forEach(function(element, index, array) { |
| 565 | var panel = $('<div data-panel="Custom' + index + '" class="slide-menu-panel slide-menu-custom-panel"></div>'); |
| 566 | if (element.content) { |
| 567 | $(element.content).appendTo(panel); |
| 568 | enableCustomLinks(panel); |
| 569 | } |
| 570 | else if (element.src) { |
| 571 | loadCustomPanelContent(panel, element.src); |
| 572 | } |
| 573 | panel.appendTo(panels); |
| 574 | }) |
| 575 | } |
| 576 | |
| 577 | // |
| 578 | // Themes |
| 579 | // |
| 580 | if (themes) { |
| 581 | var panel = $('<div data-panel="Themes" class="slide-menu-panel"></div>').appendTo(panels); |
| 582 | var menu = $('<ul class="slide-menu-items"></ul>').appendTo(panel); |
| 583 | themes.forEach(function(t, i) { |
| 584 | $('<li class="slide-menu-item" data-theme="' + t.theme + '" data-item="' + (i+1) + '">' + t.name + '</li>').appendTo(menu).click(clicked); |
| 585 | }) |
| 586 | } |
| 587 | |
| 588 | // |
| 589 | // Transitions |
| 590 | // |
| 591 | if (transitions) { |
| 592 | var panel = $('<div data-panel="Transitions" class="slide-menu-panel"></div>').appendTo(panels); |
| 593 | var menu = $('<ul class="slide-menu-items"></ul>').appendTo(panel); |
| 594 | ['None', 'Fade', 'Slide', 'Convex', 'Concave', 'Zoom', 'Cube', 'Page'].forEach(function(name, i) { |
| 595 | $('<li class="slide-menu-item" data-transition="' + name.toLowerCase() + '" data-item="' + (i+1) + '">' + name + '</li>').appendTo(menu).click(clicked); |
| 596 | }) |
| 597 | } |
| 598 | |
| 599 | // |
| 600 | // Open menu options |
| 601 | // |
| 602 | if (openButton) { |
| 603 | // add menu button |
| 604 | $('<div class="slide-menu-button"><a href="#"><i class="fa fa-bars"></i></a></div>') |
| 605 | .appendTo($('.reveal')) |
| 606 | .click(openMenu); |
| 607 | } |
| 608 | |
| 609 | if (openSlideNumber) { |
| 610 | // wrap slide number in link |
| 611 | $('<div class="slide-number-wrapper"><a href="#"></a></div>').insertAfter($('div.slide-number')); |
| 612 | $('.slide-number').appendTo($('.slide-number-wrapper a')); |
| 613 | $('.slide-number-wrapper a').click(openMenu); |
| 614 | } |
| 615 | |
| 616 | // |
| 617 | // Handle mouse overs |
| 618 | // |
| 619 | var mouseSelectionEnabled = true; |
| 620 | $('.slide-menu-panel .slide-menu-items li').mouseenter(function(event) { |
| 621 | if (mouseSelectionEnabled) { |
| 622 | $('.active-menu-panel .slide-menu-items li').removeClass('selected'); |
| 623 | $(event.currentTarget).addClass('selected'); |
| 624 | } |
| 625 | }); |
| 626 | |
| 627 | module.toggle = toggleMenu; |
| 628 | module.isOpen = isOpen; |
| 629 | |
| 630 | /** |
| 631 | * Extend object a with the properties of object b. |
| 632 | * If there's a conflict, object b takes precedence. |
| 633 | */ |
| 634 | function extend( a, b ) { |
| 635 | for( var i in b ) { |
| 636 | a[ i ] = b[ i ]; |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | /** |
| 641 | * Dispatches an event of the specified type from the |
| 642 | * reveal DOM element. |
| 643 | */ |
| 644 | function dispatchEvent( type, args ) { |
| 645 | var event = document.createEvent( 'HTMLEvents', 1, 2 ); |
| 646 | event.initEvent( type, true, true ); |
| 647 | extend( event, args ); |
| 648 | document.querySelector('.reveal').dispatchEvent( event ); |
| 649 | |
| 650 | // If we're in an iframe, post each reveal.js event to the |
| 651 | // parent window. Used by the notes plugin |
| 652 | if( config.postMessageEvents && window.parent !== window.self ) { |
| 653 | window.parent.postMessage( JSON.stringify({ namespace: 'reveal', eventName: type, state: getState() }), '*' ); |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | dispatchEvent('menu-ready'); |
| 658 | } |
| 659 | }) |
| 660 | }) |
| 661 | }); |
| 662 | |
| 663 | // modified from math plugin |
| 664 | function loadResource( url, type, callback ) { |
| 665 | var head = document.querySelector( 'head' ); |
| 666 | var resource; |
| 667 | |
| 668 | if ( type === 'script' ) { |
| 669 | resource = document.createElement( 'script' ); |
| 670 | resource.type = 'text/javascript'; |
| 671 | resource.src = url; |
| 672 | } |
| 673 | else if ( type === 'stylesheet' ) { |
| 674 | resource = document.createElement( 'link' ); |
| 675 | resource.rel = 'stylesheet'; |
| 676 | resource.href = url; |
| 677 | } |
| 678 | |
| 679 | // Wrapper for callback to make sure it only fires once |
| 680 | var finish = function() { |
| 681 | if( typeof callback === 'function' ) { |
| 682 | callback.call(); |
| 683 | callback = null; |
| 684 | } |
| 685 | } |
| 686 | |
| 687 | resource.onload = finish; |
| 688 | |
| 689 | // IE |
| 690 | resource.onreadystatechange = function() { |
| 691 | if ( this.readyState === 'loaded' ) { |
| 692 | finish(); |
| 693 | } |
| 694 | } |
| 695 | |
| 696 | // Normal browsers |
| 697 | head.appendChild( resource ); |
| 698 | } |
| 699 | |
| 700 | function scriptPath() { |
| 701 | // obtain plugin path from the script element |
| 702 | var path; |
| 703 | if (document.currentScript) { |
| 704 | path = document.currentScript.src.slice(0, -7); |
| 705 | } else { |
| 706 | var sel = document.querySelector('script[src$="/menu.js"]') |
| 707 | if (sel) { |
| 708 | path = sel.src.slice(0, -7); |
| 709 | } |
| 710 | } |
| 711 | return path; |
| 712 | } |
| 713 | |
| 714 | return module; |
| 715 | })(); |