// Install some useful jQuery extensions that we use a lot $.support.touch = 'ontouchstart' in window; $.extend($.fn, { orNull: function() { return this.length > 0 ? this : null; }, findAndSelf: function(selector) { return this.find(selector).add(this.filter(selector)); } }); // Little Helpers function hyphenate(str) { return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); } function isVisible(el) { if (el.is(':hidden')) return false; var viewTop = $(window).scrollTop(); var viewBottom = viewTop + $(window).height(); var top = el.offset().top; var bottom = top + el.height(); return top >= viewTop && bottom <= viewBottom || top <= viewTop && bottom >= viewTop || top <= viewBottom && bottom >= viewBottom; } function smoothScrollTo(el, callback) { $('html, body').animate({ scrollTop: el.offset().top }, 250, callback); } var behaviors = {}; behaviors.hiDPI = function() { // Turn off hiDPI for all touch devices for now, until the site is built // true to scale. if ($.support.touch) $('canvas').attr('hidpi', 'off'); }; behaviors.sections = function() { var toc = $('.toc'); var checks = []; var active; function update() { $.each(checks, function() { if (this()) return false; }); } $(document).scroll(update); $(window).resize(update); setTimeout(update, 0); $('article section').each(function() { var section = $(this); var anchor = $('a', section); // Move content until next section inside section section.append(section.nextUntil('section')); var title = anchor.attr('title') || $('h1,h2', section).first().text(); var id = section.attr('id'); if (!id) { id = hyphenate(title) .replace(/\s+/g, '-') .replace(/^#/, '') .replace(/[!"#$%&'\()*+,.\/:;<=>?@\[\\\]\^_`{|}~]+/g, '-') .replace(/-+/g, '-'); section.attr('id', id); anchor.attr('name', id); } function activate() { if (active) active.removeClass('active'); selector.addClass('active'); active = selector; } // Create table of contents on the fly if (toc) { var selector = $('
  • ' + title + '
  • ').appendTo(toc); if (section.is('.spacer')) selector.addClass('spacer'); $('a', selector).click(function() { smoothScrollTo(section, function() { window.location.hash = id; }); return false; }); checks.push(function() { var visible = isVisible(section); if (visible) activate(); return visible; }); } }); // Adjust height of last section so that the last anchor aligns perfectly // with the top of the browser window. var lastSection = $('article section:last'); var lastAnchor = $('a[name]', lastSection); function resize() { lastSection.height('auto'); var bottom = $(document).height() - lastAnchor.offset().top - $(window).height(); if (bottom < 0) lastSection.height(lastSection.height() - bottom); } if (lastSection.length && lastAnchor.length) { $(window).on({ load: resize, resize: resize }); resize(); } }; behaviors.sticky = function() { $('.sticky').each(function() { me = $(this); container = $('
    ').append(me.contents()).appendTo(me); // Insert a div wrapper of which the fixed class is modified depending on position $(window).scroll(function() { if (container.is(':visible')) container.toggleClass('fixed', me.offset().top - $(this).scrollTop() <= 0); }); }); }; behaviors.hash = function() { var hash = unescape(window.location.hash); if (hash) { // First see if there's a class member to open var target = $(hash); if (target.length) { if (target.hasClass('member')) toggleMember(target); smoothScrollTo(target); } } }; behaviors.thumbnails = function() { var thumbnails = $('.thumbnail'); var height = 0; thumbnails.each(function() { height = Math.max(height, $(this).height()); }); $('.thumbnail').height(height); }; behaviors.expandableLists = function() { $('.expandable-list').each(function() { var list = $(this); $('') .prependTo(list) .click(function() { list.toggleClass('expanded'); }); }); }; behaviors.referenceClass = function() { var classes = $('.reference-classes'); if (classes.length) { // Mark currently selected class as active. Do it client-sided // since the menu is generated by jsdocs. var path = window.location.pathname.toLowerCase(); $('a[href="' + path + '"]', classes).addClass('active'); } }; behaviors.hover = function() { $('.hover').hover(function() { $('.normal', this).toggleClass('hidden'); $('.over', this).toggleClass('hidden'); }); }; behaviors.code = function() { $('.code:visible, pre:visible code').each(function() { createCode($(this)); }); }; behaviors.paperscript = function() { // Ignore all paperscripts in the automatic load event, and load them // separately in createPaperScript() when needed. $('script[type="text/paperscript"]').attr('ignore', 'true'); $('.paperscript:visible').each(function() { createPaperScript($(this)); }); }; function createCodeMirror(place, options, source) { return new CodeMirror(place, $.extend({}, { mode: 'javascript', lineNumbers: true, matchBrackets: true, tabSize: 4, indentUnit: 4, indentWithTabs: true, tabMode: 'shift', value: source.text().match( // Remove first & last empty line /^\s*?[\n\r]?([\u0000-\uffff]*?)[\n\r]?\s*?$/)[1] }, options)); } function createCode(element) { if (element.data('initialized')) return; var start = element.attr('start'); var highlight = element.attr('highlight'); var editor = createCodeMirror(function(el) { element.replaceWith(el); }, { lineNumbers: !element.parent('.resource-text').length, firstLineNumber: parseInt(start || 1, 10), mode: element.attr('mode') || 'javascript', readOnly: true }, element); if (highlight) { var highlights = highlight.split(','); for (var i = 0, l = highlights.length; i < l; i++) { var highlight = highlights[i].split('-'); var hlStart = parseInt(highlight[0], 10) - 1; var hlEnd = highlight.length == 2 ? parseInt(highlight[1], 10) - 1 : hlStart; if (start) { hlStart -= start - 1; hlEnd -= start - 1; } for (var j = hlStart; j <= hlEnd; j++) { editor.setLineClass(j, 'highlight'); } } } element.data('initialized', true); } function createPaperScript(element) { if (element.data('initialized')) return; var script = $('script', element).orNull(), runButton = $('.button.run', element).orNull(); if (!script) return; // Now load / parse / execute the script script.removeAttr('ignore'); var scope = paper.PaperScript.load(script[0]); if (!runButton) return; var canvas = $('canvas', element), hasResize = canvas.attr('resize'), showSplit = element.hasClass('split'), sourceFirst = element.hasClass('source'), editor = null, hasBorders = true, edited = false, animateExplain, explain = $('.explain', element).orNull(), source = $('