mirror of
https://github.com/danbulant/api_docs
synced 2026-06-20 06:41:40 +00:00
Static table of contents (#701)
* Add showing/hiding submenus, fix sidebar styling, fix bug where includes wouldn't appear in ToC * Update ToC to highlight last header if page is scrolled to very bottom, fixes #280 * Set HTML title to current h1 section text, see #133 * Fix menu not opening on mobile * Add back increase toc item height on mobile * Fix padding bug * Add back in ToC sliding animation
This commit is contained in:
parent
e7f5144e4c
commit
53e2f23e5c
13 changed files with 203 additions and 1698 deletions
1
Gemfile
1
Gemfile
|
|
@ -7,3 +7,4 @@ gem 'middleman-autoprefixer', '~> 2.7.0'
|
||||||
gem "middleman-sprockets", "~> 4.1.0"
|
gem "middleman-sprockets", "~> 4.1.0"
|
||||||
gem 'rouge', '~> 2.0.5'
|
gem 'rouge', '~> 2.0.5'
|
||||||
gem 'redcarpet', '~> 3.4.0'
|
gem 'redcarpet', '~> 3.4.0'
|
||||||
|
gem 'nokogiri', '~> 1.6.8'
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,10 @@ GEM
|
||||||
middleman-syntax (3.0.0)
|
middleman-syntax (3.0.0)
|
||||||
middleman-core (>= 3.2)
|
middleman-core (>= 3.2)
|
||||||
rouge (~> 2.0)
|
rouge (~> 2.0)
|
||||||
|
mini_portile2 (2.1.0)
|
||||||
minitest (5.10.1)
|
minitest (5.10.1)
|
||||||
|
nokogiri (1.6.8.1)
|
||||||
|
mini_portile2 (~> 2.1.0)
|
||||||
padrino-helpers (0.13.3.3)
|
padrino-helpers (0.13.3.3)
|
||||||
i18n (~> 0.6, >= 0.6.7)
|
i18n (~> 0.6, >= 0.6.7)
|
||||||
padrino-support (= 0.13.3.3)
|
padrino-support (= 0.13.3.3)
|
||||||
|
|
@ -115,8 +118,9 @@ DEPENDENCIES
|
||||||
middleman-autoprefixer (~> 2.7.0)
|
middleman-autoprefixer (~> 2.7.0)
|
||||||
middleman-sprockets (~> 4.1.0)
|
middleman-sprockets (~> 4.1.0)
|
||||||
middleman-syntax (~> 3.0.0)
|
middleman-syntax (~> 3.0.0)
|
||||||
|
nokogiri (~> 1.6.8)
|
||||||
redcarpet (~> 3.4.0)
|
redcarpet (~> 3.4.0)
|
||||||
rouge (~> 2.0.5)
|
rouge (~> 2.0.5)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.14.3
|
1.14.5
|
||||||
|
|
|
||||||
|
|
@ -47,3 +47,7 @@ end
|
||||||
# Deploy Configuration
|
# Deploy Configuration
|
||||||
# If you want Middleman to listen on a different port, you can set that below
|
# If you want Middleman to listen on a different port, you can set that below
|
||||||
set :port, 4567
|
set :port, 4567
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
require './lib/toc_data.rb'
|
||||||
|
end
|
||||||
|
|
|
||||||
30
lib/toc_data.rb
Normal file
30
lib/toc_data.rb
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
|
def toc_data(page_content)
|
||||||
|
html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content)
|
||||||
|
|
||||||
|
# get a flat list of headers
|
||||||
|
headers = []
|
||||||
|
html_doc.css('h1, h2, h3').each do |header|
|
||||||
|
headers.push({
|
||||||
|
id: header.attribute('id').to_s,
|
||||||
|
content: header.content,
|
||||||
|
level: header.name[1].to_i,
|
||||||
|
children: []
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
[3,2].each do |header_level|
|
||||||
|
header_to_nest = nil
|
||||||
|
headers = headers.reject do |header|
|
||||||
|
if header[:level] == header_level
|
||||||
|
header_to_nest[:children].push header if header_to_nest
|
||||||
|
true
|
||||||
|
else
|
||||||
|
header_to_nest = header if header[:level] == (header_level - 1)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
headers
|
||||||
|
end
|
||||||
|
|
@ -1,4 +1,2 @@
|
||||||
//= require ./lib/_energize
|
//= require ./all_nosearch
|
||||||
//= require ./app/_lang
|
|
||||||
//= require ./app/_search
|
//= require ./app/_search
|
||||||
//= require ./app/_toc
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,16 @@
|
||||||
//= require ./lib/_energize
|
//= require ./lib/_energize
|
||||||
//= require ./app/_lang
|
|
||||||
//= require ./app/_toc
|
//= require ./app/_toc
|
||||||
|
//= require ./app/_lang
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
loadToc($('#toc'), '.toc-link', '.toc-list-h2', 10);
|
||||||
|
setupLanguages($('body').data('languages'));
|
||||||
|
$('.content').imagesLoaded( function() {
|
||||||
|
window.recacheHeights();
|
||||||
|
window.refreshToc();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
window.onpopstate = function() {
|
||||||
|
activateLanguage(getLanguageFromQueryString());
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,14 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
License for the specific language governing permissions and limitations
|
License for the specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
(function (global) {
|
;(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var languages = [];
|
var languages = [];
|
||||||
|
|
||||||
global.setupLanguages = setupLanguages;
|
window.setupLanguages = setupLanguages;
|
||||||
global.activateLanguage = activateLanguage;
|
window.activateLanguage = activateLanguage;
|
||||||
|
window.getLanguageFromQueryString = getLanguageFromQueryString;
|
||||||
|
|
||||||
function activateLanguage(language) {
|
function activateLanguage(language) {
|
||||||
if (!language) return;
|
if (!language) return;
|
||||||
|
|
@ -36,7 +37,7 @@ under the License.
|
||||||
$(".highlight.tab-" + language).show();
|
$(".highlight.tab-" + language).show();
|
||||||
$(".lang-specific." + language).show();
|
$(".lang-specific." + language).show();
|
||||||
|
|
||||||
global.toc.calculateHeights();
|
window.recacheHeights();
|
||||||
|
|
||||||
// scroll to the new location of the position
|
// scroll to the new location of the position
|
||||||
if ($(window.location.hash).get(0)) {
|
if ($(window.location.hash).get(0)) {
|
||||||
|
|
@ -159,8 +160,5 @@ under the License.
|
||||||
activateLanguage(language);
|
activateLanguage(language);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
window.onpopstate = function() {
|
|
||||||
activateLanguage(getLanguageFromQueryString());
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
})(window);
|
})();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//= require ../lib/_lunr
|
//= require ../lib/_lunr
|
||||||
//= require ../lib/_jquery
|
//= require ../lib/_jquery
|
||||||
//= require ../lib/_jquery.highlight
|
//= require ../lib/_jquery.highlight
|
||||||
(function () {
|
;(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var content, searchResults;
|
var content, searchResults;
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,106 @@
|
||||||
//= require ../lib/_jquery
|
//= require ../lib/_jquery
|
||||||
//= require ../lib/_jquery_ui
|
|
||||||
//= require ../lib/_jquery.tocify
|
|
||||||
//= require ../lib/_imagesloaded.min
|
//= require ../lib/_imagesloaded.min
|
||||||
(function (global) {
|
;(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var debounce = function(func, waitTime) {
|
||||||
|
var timeout = false;
|
||||||
|
return function() {
|
||||||
|
if (timeout === false) {
|
||||||
|
setTimeout(function() {
|
||||||
|
func();
|
||||||
|
timeout = false;
|
||||||
|
}, waitTime);
|
||||||
|
timeout = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var closeToc = function() {
|
var closeToc = function() {
|
||||||
$(".tocify-wrapper").removeClass('open');
|
$(".tocify-wrapper").removeClass('open');
|
||||||
$("#nav-button").removeClass('open');
|
$("#nav-button").removeClass('open');
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeToc = function() {
|
function loadToc($toc, tocLinkSelector, tocListSelector, scrollOffset) {
|
||||||
global.toc = $("#toc").tocify({
|
var headerHeights = {};
|
||||||
selectors: 'h1, h2',
|
var pageHeight = 0;
|
||||||
extendPage: false,
|
var windowHeight = 0;
|
||||||
theme: 'none',
|
var originalTitle = document.title;
|
||||||
smoothScroll: false,
|
|
||||||
showEffectSpeed: 0,
|
var recacheHeights = function() {
|
||||||
hideEffectSpeed: 180,
|
headerHeights = {};
|
||||||
ignoreSelector: '.toc-ignore',
|
pageHeight = $(document).height();
|
||||||
highlightOffset: 60,
|
windowHeight = $(window).height();
|
||||||
scrollTo: -1,
|
|
||||||
scrollHistory: true,
|
$toc.find(tocLinkSelector).each(function() {
|
||||||
hashGenerator: function (text, element) {
|
var targetId = $(this).attr('href');
|
||||||
return element.prop('id');
|
if (targetId[0] === "#") {
|
||||||
|
headerHeights[targetId] = $(targetId).offset().top;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var refreshToc = function() {
|
||||||
|
var currentTop = $(document).scrollTop() + scrollOffset;
|
||||||
|
|
||||||
|
if (currentTop + windowHeight >= pageHeight) {
|
||||||
|
// at bottom of page, so just select last header by making currentTop very large
|
||||||
|
// this fixes the problem where the last header won't ever show as active if its content
|
||||||
|
// is shorter than the window height
|
||||||
|
currentTop = pageHeight + 1000;
|
||||||
}
|
}
|
||||||
}).data('toc-tocify');
|
|
||||||
|
|
||||||
$("#nav-button").click(function() {
|
var best = null;
|
||||||
$(".tocify-wrapper").toggleClass('open');
|
for (var name in headerHeights) {
|
||||||
$("#nav-button").toggleClass('open');
|
if ((headerHeights[name] < currentTop && headerHeights[name] > headerHeights[best]) || best === null) {
|
||||||
return false;
|
best = name;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(".page-wrapper").click(closeToc);
|
var $best = $toc.find("[href='" + best + "']").first();
|
||||||
$(".tocify-item").click(closeToc);
|
if (!$best.hasClass("active")) {
|
||||||
};
|
$toc.find(".active").removeClass("active");
|
||||||
|
$best.addClass("active");
|
||||||
|
$best.parents(tocListSelector).addClass("active");
|
||||||
|
$best.siblings(tocListSelector).addClass("active");
|
||||||
|
$toc.find(tocListSelector).filter(":not(.active)").slideUp(150);
|
||||||
|
$toc.find(tocListSelector).filter(".active").slideDown(150);
|
||||||
|
if (window.history.pushState) {
|
||||||
|
window.history.pushState(null, "", best);
|
||||||
|
}
|
||||||
|
// TODO remove classnames
|
||||||
|
document.title = $best.data("title") + " – " + originalTitle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Hack to make already open sections to start opened,
|
var makeToc = function() {
|
||||||
// instead of displaying an ugly animation
|
recacheHeights();
|
||||||
function animate() {
|
refreshToc();
|
||||||
setTimeout(function() {
|
|
||||||
toc.setOption('showEffectSpeed', 180);
|
$("#nav-button").click(function() {
|
||||||
}, 50);
|
$(".toc-wrapper").toggleClass('open');
|
||||||
|
$("#nav-button").toggleClass('open');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$(".page-wrapper").click(closeToc);
|
||||||
|
$(".tocify-item").click(closeToc);
|
||||||
|
|
||||||
|
// reload immediately after scrolling on toc click
|
||||||
|
$toc.find(tocLinkSelector).click(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
refreshToc();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).scroll(debounce(refreshToc, 200));
|
||||||
|
$(window).resize(debounce(recacheHeights, 200));
|
||||||
|
};
|
||||||
|
|
||||||
|
makeToc();
|
||||||
|
|
||||||
|
window.recacheHeights = recacheHeights;
|
||||||
|
window.refreshToc = refreshToc;
|
||||||
}
|
}
|
||||||
|
|
||||||
$(function() {
|
window.loadToc = loadToc;
|
||||||
makeToc();
|
})();
|
||||||
animate();
|
|
||||||
setupLanguages($('body').data('languages'));
|
|
||||||
$('.content').imagesLoaded( function() {
|
|
||||||
global.toc.calculateHeights();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(window);
|
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,566 +0,0 @@
|
||||||
/*! jQuery UI - v1.11.3 - 2015-02-12
|
|
||||||
* http://jqueryui.com
|
|
||||||
* Includes: widget.js
|
|
||||||
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
|
|
||||||
|
|
||||||
(function( factory ) {
|
|
||||||
if ( typeof define === "function" && define.amd ) {
|
|
||||||
|
|
||||||
// AMD. Register as an anonymous module.
|
|
||||||
define([ "jquery" ], factory );
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Browser globals
|
|
||||||
factory( jQuery );
|
|
||||||
}
|
|
||||||
}(function( $ ) {
|
|
||||||
/*!
|
|
||||||
* jQuery UI Widget 1.11.3
|
|
||||||
* http://jqueryui.com
|
|
||||||
*
|
|
||||||
* Copyright jQuery Foundation and other contributors
|
|
||||||
* Released under the MIT license.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://api.jqueryui.com/jQuery.widget/
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
var widget_uuid = 0,
|
|
||||||
widget_slice = Array.prototype.slice;
|
|
||||||
|
|
||||||
$.cleanData = (function( orig ) {
|
|
||||||
return function( elems ) {
|
|
||||||
var events, elem, i;
|
|
||||||
for ( i = 0; (elem = elems[i]) != null; i++ ) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
// Only trigger remove when necessary to save time
|
|
||||||
events = $._data( elem, "events" );
|
|
||||||
if ( events && events.remove ) {
|
|
||||||
$( elem ).triggerHandler( "remove" );
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://bugs.jquery.com/ticket/8235
|
|
||||||
} catch ( e ) {}
|
|
||||||
}
|
|
||||||
orig( elems );
|
|
||||||
};
|
|
||||||
})( $.cleanData );
|
|
||||||
|
|
||||||
$.widget = function( name, base, prototype ) {
|
|
||||||
var fullName, existingConstructor, constructor, basePrototype,
|
|
||||||
// proxiedPrototype allows the provided prototype to remain unmodified
|
|
||||||
// so that it can be used as a mixin for multiple widgets (#8876)
|
|
||||||
proxiedPrototype = {},
|
|
||||||
namespace = name.split( "." )[ 0 ];
|
|
||||||
|
|
||||||
name = name.split( "." )[ 1 ];
|
|
||||||
fullName = namespace + "-" + name;
|
|
||||||
|
|
||||||
if ( !prototype ) {
|
|
||||||
prototype = base;
|
|
||||||
base = $.Widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create selector for plugin
|
|
||||||
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
|
|
||||||
return !!$.data( elem, fullName );
|
|
||||||
};
|
|
||||||
|
|
||||||
$[ namespace ] = $[ namespace ] || {};
|
|
||||||
existingConstructor = $[ namespace ][ name ];
|
|
||||||
constructor = $[ namespace ][ name ] = function( options, element ) {
|
|
||||||
// allow instantiation without "new" keyword
|
|
||||||
if ( !this._createWidget ) {
|
|
||||||
return new constructor( options, element );
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow instantiation without initializing for simple inheritance
|
|
||||||
// must use "new" keyword (the code above always passes args)
|
|
||||||
if ( arguments.length ) {
|
|
||||||
this._createWidget( options, element );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// extend with the existing constructor to carry over any static properties
|
|
||||||
$.extend( constructor, existingConstructor, {
|
|
||||||
version: prototype.version,
|
|
||||||
// copy the object used to create the prototype in case we need to
|
|
||||||
// redefine the widget later
|
|
||||||
_proto: $.extend( {}, prototype ),
|
|
||||||
// track widgets that inherit from this widget in case this widget is
|
|
||||||
// redefined after a widget inherits from it
|
|
||||||
_childConstructors: []
|
|
||||||
});
|
|
||||||
|
|
||||||
basePrototype = new base();
|
|
||||||
// we need to make the options hash a property directly on the new instance
|
|
||||||
// otherwise we'll modify the options hash on the prototype that we're
|
|
||||||
// inheriting from
|
|
||||||
basePrototype.options = $.widget.extend( {}, basePrototype.options );
|
|
||||||
$.each( prototype, function( prop, value ) {
|
|
||||||
if ( !$.isFunction( value ) ) {
|
|
||||||
proxiedPrototype[ prop ] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
proxiedPrototype[ prop ] = (function() {
|
|
||||||
var _super = function() {
|
|
||||||
return base.prototype[ prop ].apply( this, arguments );
|
|
||||||
},
|
|
||||||
_superApply = function( args ) {
|
|
||||||
return base.prototype[ prop ].apply( this, args );
|
|
||||||
};
|
|
||||||
return function() {
|
|
||||||
var __super = this._super,
|
|
||||||
__superApply = this._superApply,
|
|
||||||
returnValue;
|
|
||||||
|
|
||||||
this._super = _super;
|
|
||||||
this._superApply = _superApply;
|
|
||||||
|
|
||||||
returnValue = value.apply( this, arguments );
|
|
||||||
|
|
||||||
this._super = __super;
|
|
||||||
this._superApply = __superApply;
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
constructor.prototype = $.widget.extend( basePrototype, {
|
|
||||||
// TODO: remove support for widgetEventPrefix
|
|
||||||
// always use the name + a colon as the prefix, e.g., draggable:start
|
|
||||||
// don't prefix for widgets that aren't DOM-based
|
|
||||||
widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
|
|
||||||
}, proxiedPrototype, {
|
|
||||||
constructor: constructor,
|
|
||||||
namespace: namespace,
|
|
||||||
widgetName: name,
|
|
||||||
widgetFullName: fullName
|
|
||||||
});
|
|
||||||
|
|
||||||
// If this widget is being redefined then we need to find all widgets that
|
|
||||||
// are inheriting from it and redefine all of them so that they inherit from
|
|
||||||
// the new version of this widget. We're essentially trying to replace one
|
|
||||||
// level in the prototype chain.
|
|
||||||
if ( existingConstructor ) {
|
|
||||||
$.each( existingConstructor._childConstructors, function( i, child ) {
|
|
||||||
var childPrototype = child.prototype;
|
|
||||||
|
|
||||||
// redefine the child widget using the same prototype that was
|
|
||||||
// originally used, but inherit from the new version of the base
|
|
||||||
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
|
|
||||||
});
|
|
||||||
// remove the list of existing child constructors from the old constructor
|
|
||||||
// so the old child constructors can be garbage collected
|
|
||||||
delete existingConstructor._childConstructors;
|
|
||||||
} else {
|
|
||||||
base._childConstructors.push( constructor );
|
|
||||||
}
|
|
||||||
|
|
||||||
$.widget.bridge( name, constructor );
|
|
||||||
|
|
||||||
return constructor;
|
|
||||||
};
|
|
||||||
|
|
||||||
$.widget.extend = function( target ) {
|
|
||||||
var input = widget_slice.call( arguments, 1 ),
|
|
||||||
inputIndex = 0,
|
|
||||||
inputLength = input.length,
|
|
||||||
key,
|
|
||||||
value;
|
|
||||||
for ( ; inputIndex < inputLength; inputIndex++ ) {
|
|
||||||
for ( key in input[ inputIndex ] ) {
|
|
||||||
value = input[ inputIndex ][ key ];
|
|
||||||
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
|
|
||||||
// Clone objects
|
|
||||||
if ( $.isPlainObject( value ) ) {
|
|
||||||
target[ key ] = $.isPlainObject( target[ key ] ) ?
|
|
||||||
$.widget.extend( {}, target[ key ], value ) :
|
|
||||||
// Don't extend strings, arrays, etc. with objects
|
|
||||||
$.widget.extend( {}, value );
|
|
||||||
// Copy everything else by reference
|
|
||||||
} else {
|
|
||||||
target[ key ] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
|
|
||||||
$.widget.bridge = function( name, object ) {
|
|
||||||
var fullName = object.prototype.widgetFullName || name;
|
|
||||||
$.fn[ name ] = function( options ) {
|
|
||||||
var isMethodCall = typeof options === "string",
|
|
||||||
args = widget_slice.call( arguments, 1 ),
|
|
||||||
returnValue = this;
|
|
||||||
|
|
||||||
if ( isMethodCall ) {
|
|
||||||
this.each(function() {
|
|
||||||
var methodValue,
|
|
||||||
instance = $.data( this, fullName );
|
|
||||||
if ( options === "instance" ) {
|
|
||||||
returnValue = instance;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !instance ) {
|
|
||||||
return $.error( "cannot call methods on " + name + " prior to initialization; " +
|
|
||||||
"attempted to call method '" + options + "'" );
|
|
||||||
}
|
|
||||||
if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
|
|
||||||
return $.error( "no such method '" + options + "' for " + name + " widget instance" );
|
|
||||||
}
|
|
||||||
methodValue = instance[ options ].apply( instance, args );
|
|
||||||
if ( methodValue !== instance && methodValue !== undefined ) {
|
|
||||||
returnValue = methodValue && methodValue.jquery ?
|
|
||||||
returnValue.pushStack( methodValue.get() ) :
|
|
||||||
methodValue;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Allow multiple hashes to be passed on init
|
|
||||||
if ( args.length ) {
|
|
||||||
options = $.widget.extend.apply( null, [ options ].concat(args) );
|
|
||||||
}
|
|
||||||
|
|
||||||
this.each(function() {
|
|
||||||
var instance = $.data( this, fullName );
|
|
||||||
if ( instance ) {
|
|
||||||
instance.option( options || {} );
|
|
||||||
if ( instance._init ) {
|
|
||||||
instance._init();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$.data( this, fullName, new object( options, this ) );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
$.Widget = function( /* options, element */ ) {};
|
|
||||||
$.Widget._childConstructors = [];
|
|
||||||
|
|
||||||
$.Widget.prototype = {
|
|
||||||
widgetName: "widget",
|
|
||||||
widgetEventPrefix: "",
|
|
||||||
defaultElement: "<div>",
|
|
||||||
options: {
|
|
||||||
disabled: false,
|
|
||||||
|
|
||||||
// callbacks
|
|
||||||
create: null
|
|
||||||
},
|
|
||||||
_createWidget: function( options, element ) {
|
|
||||||
element = $( element || this.defaultElement || this )[ 0 ];
|
|
||||||
this.element = $( element );
|
|
||||||
this.uuid = widget_uuid++;
|
|
||||||
this.eventNamespace = "." + this.widgetName + this.uuid;
|
|
||||||
|
|
||||||
this.bindings = $();
|
|
||||||
this.hoverable = $();
|
|
||||||
this.focusable = $();
|
|
||||||
|
|
||||||
if ( element !== this ) {
|
|
||||||
$.data( element, this.widgetFullName, this );
|
|
||||||
this._on( true, this.element, {
|
|
||||||
remove: function( event ) {
|
|
||||||
if ( event.target === element ) {
|
|
||||||
this.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.document = $( element.style ?
|
|
||||||
// element within the document
|
|
||||||
element.ownerDocument :
|
|
||||||
// element is window or document
|
|
||||||
element.document || element );
|
|
||||||
this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
|
|
||||||
}
|
|
||||||
|
|
||||||
this.options = $.widget.extend( {},
|
|
||||||
this.options,
|
|
||||||
this._getCreateOptions(),
|
|
||||||
options );
|
|
||||||
|
|
||||||
this._create();
|
|
||||||
this._trigger( "create", null, this._getCreateEventData() );
|
|
||||||
this._init();
|
|
||||||
},
|
|
||||||
_getCreateOptions: $.noop,
|
|
||||||
_getCreateEventData: $.noop,
|
|
||||||
_create: $.noop,
|
|
||||||
_init: $.noop,
|
|
||||||
|
|
||||||
destroy: function() {
|
|
||||||
this._destroy();
|
|
||||||
// we can probably remove the unbind calls in 2.0
|
|
||||||
// all event bindings should go through this._on()
|
|
||||||
this.element
|
|
||||||
.unbind( this.eventNamespace )
|
|
||||||
.removeData( this.widgetFullName )
|
|
||||||
// support: jquery <1.6.3
|
|
||||||
// http://bugs.jquery.com/ticket/9413
|
|
||||||
.removeData( $.camelCase( this.widgetFullName ) );
|
|
||||||
this.widget()
|
|
||||||
.unbind( this.eventNamespace )
|
|
||||||
.removeAttr( "aria-disabled" )
|
|
||||||
.removeClass(
|
|
||||||
this.widgetFullName + "-disabled " +
|
|
||||||
"ui-state-disabled" );
|
|
||||||
|
|
||||||
// clean up events and states
|
|
||||||
this.bindings.unbind( this.eventNamespace );
|
|
||||||
this.hoverable.removeClass( "ui-state-hover" );
|
|
||||||
this.focusable.removeClass( "ui-state-focus" );
|
|
||||||
},
|
|
||||||
_destroy: $.noop,
|
|
||||||
|
|
||||||
widget: function() {
|
|
||||||
return this.element;
|
|
||||||
},
|
|
||||||
|
|
||||||
option: function( key, value ) {
|
|
||||||
var options = key,
|
|
||||||
parts,
|
|
||||||
curOption,
|
|
||||||
i;
|
|
||||||
|
|
||||||
if ( arguments.length === 0 ) {
|
|
||||||
// don't return a reference to the internal hash
|
|
||||||
return $.widget.extend( {}, this.options );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( typeof key === "string" ) {
|
|
||||||
// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
|
|
||||||
options = {};
|
|
||||||
parts = key.split( "." );
|
|
||||||
key = parts.shift();
|
|
||||||
if ( parts.length ) {
|
|
||||||
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
|
|
||||||
for ( i = 0; i < parts.length - 1; i++ ) {
|
|
||||||
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
|
|
||||||
curOption = curOption[ parts[ i ] ];
|
|
||||||
}
|
|
||||||
key = parts.pop();
|
|
||||||
if ( arguments.length === 1 ) {
|
|
||||||
return curOption[ key ] === undefined ? null : curOption[ key ];
|
|
||||||
}
|
|
||||||
curOption[ key ] = value;
|
|
||||||
} else {
|
|
||||||
if ( arguments.length === 1 ) {
|
|
||||||
return this.options[ key ] === undefined ? null : this.options[ key ];
|
|
||||||
}
|
|
||||||
options[ key ] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setOptions( options );
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
_setOptions: function( options ) {
|
|
||||||
var key;
|
|
||||||
|
|
||||||
for ( key in options ) {
|
|
||||||
this._setOption( key, options[ key ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
_setOption: function( key, value ) {
|
|
||||||
this.options[ key ] = value;
|
|
||||||
|
|
||||||
if ( key === "disabled" ) {
|
|
||||||
this.widget()
|
|
||||||
.toggleClass( this.widgetFullName + "-disabled", !!value );
|
|
||||||
|
|
||||||
// If the widget is becoming disabled, then nothing is interactive
|
|
||||||
if ( value ) {
|
|
||||||
this.hoverable.removeClass( "ui-state-hover" );
|
|
||||||
this.focusable.removeClass( "ui-state-focus" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
enable: function() {
|
|
||||||
return this._setOptions({ disabled: false });
|
|
||||||
},
|
|
||||||
disable: function() {
|
|
||||||
return this._setOptions({ disabled: true });
|
|
||||||
},
|
|
||||||
|
|
||||||
_on: function( suppressDisabledCheck, element, handlers ) {
|
|
||||||
var delegateElement,
|
|
||||||
instance = this;
|
|
||||||
|
|
||||||
// no suppressDisabledCheck flag, shuffle arguments
|
|
||||||
if ( typeof suppressDisabledCheck !== "boolean" ) {
|
|
||||||
handlers = element;
|
|
||||||
element = suppressDisabledCheck;
|
|
||||||
suppressDisabledCheck = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no element argument, shuffle and use this.element
|
|
||||||
if ( !handlers ) {
|
|
||||||
handlers = element;
|
|
||||||
element = this.element;
|
|
||||||
delegateElement = this.widget();
|
|
||||||
} else {
|
|
||||||
element = delegateElement = $( element );
|
|
||||||
this.bindings = this.bindings.add( element );
|
|
||||||
}
|
|
||||||
|
|
||||||
$.each( handlers, function( event, handler ) {
|
|
||||||
function handlerProxy() {
|
|
||||||
// allow widgets to customize the disabled handling
|
|
||||||
// - disabled as an array instead of boolean
|
|
||||||
// - disabled class as method for disabling individual parts
|
|
||||||
if ( !suppressDisabledCheck &&
|
|
||||||
( instance.options.disabled === true ||
|
|
||||||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
|
||||||
.apply( instance, arguments );
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy the guid so direct unbinding works
|
|
||||||
if ( typeof handler !== "string" ) {
|
|
||||||
handlerProxy.guid = handler.guid =
|
|
||||||
handler.guid || handlerProxy.guid || $.guid++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
|
|
||||||
eventName = match[1] + instance.eventNamespace,
|
|
||||||
selector = match[2];
|
|
||||||
if ( selector ) {
|
|
||||||
delegateElement.delegate( selector, eventName, handlerProxy );
|
|
||||||
} else {
|
|
||||||
element.bind( eventName, handlerProxy );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_off: function( element, eventName ) {
|
|
||||||
eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
|
|
||||||
this.eventNamespace;
|
|
||||||
element.unbind( eventName ).undelegate( eventName );
|
|
||||||
|
|
||||||
// Clear the stack to avoid memory leaks (#10056)
|
|
||||||
this.bindings = $( this.bindings.not( element ).get() );
|
|
||||||
this.focusable = $( this.focusable.not( element ).get() );
|
|
||||||
this.hoverable = $( this.hoverable.not( element ).get() );
|
|
||||||
},
|
|
||||||
|
|
||||||
_delay: function( handler, delay ) {
|
|
||||||
function handlerProxy() {
|
|
||||||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
|
||||||
.apply( instance, arguments );
|
|
||||||
}
|
|
||||||
var instance = this;
|
|
||||||
return setTimeout( handlerProxy, delay || 0 );
|
|
||||||
},
|
|
||||||
|
|
||||||
_hoverable: function( element ) {
|
|
||||||
this.hoverable = this.hoverable.add( element );
|
|
||||||
this._on( element, {
|
|
||||||
mouseenter: function( event ) {
|
|
||||||
$( event.currentTarget ).addClass( "ui-state-hover" );
|
|
||||||
},
|
|
||||||
mouseleave: function( event ) {
|
|
||||||
$( event.currentTarget ).removeClass( "ui-state-hover" );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_focusable: function( element ) {
|
|
||||||
this.focusable = this.focusable.add( element );
|
|
||||||
this._on( element, {
|
|
||||||
focusin: function( event ) {
|
|
||||||
$( event.currentTarget ).addClass( "ui-state-focus" );
|
|
||||||
},
|
|
||||||
focusout: function( event ) {
|
|
||||||
$( event.currentTarget ).removeClass( "ui-state-focus" );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_trigger: function( type, event, data ) {
|
|
||||||
var prop, orig,
|
|
||||||
callback = this.options[ type ];
|
|
||||||
|
|
||||||
data = data || {};
|
|
||||||
event = $.Event( event );
|
|
||||||
event.type = ( type === this.widgetEventPrefix ?
|
|
||||||
type :
|
|
||||||
this.widgetEventPrefix + type ).toLowerCase();
|
|
||||||
// the original event may come from any element
|
|
||||||
// so we need to reset the target on the new event
|
|
||||||
event.target = this.element[ 0 ];
|
|
||||||
|
|
||||||
// copy original event properties over to the new event
|
|
||||||
orig = event.originalEvent;
|
|
||||||
if ( orig ) {
|
|
||||||
for ( prop in orig ) {
|
|
||||||
if ( !( prop in event ) ) {
|
|
||||||
event[ prop ] = orig[ prop ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.trigger( event, data );
|
|
||||||
return !( $.isFunction( callback ) &&
|
|
||||||
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
|
|
||||||
event.isDefaultPrevented() );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
|
|
||||||
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
|
|
||||||
if ( typeof options === "string" ) {
|
|
||||||
options = { effect: options };
|
|
||||||
}
|
|
||||||
var hasOptions,
|
|
||||||
effectName = !options ?
|
|
||||||
method :
|
|
||||||
options === true || typeof options === "number" ?
|
|
||||||
defaultEffect :
|
|
||||||
options.effect || defaultEffect;
|
|
||||||
options = options || {};
|
|
||||||
if ( typeof options === "number" ) {
|
|
||||||
options = { duration: options };
|
|
||||||
}
|
|
||||||
hasOptions = !$.isEmptyObject( options );
|
|
||||||
options.complete = callback;
|
|
||||||
if ( options.delay ) {
|
|
||||||
element.delay( options.delay );
|
|
||||||
}
|
|
||||||
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
|
|
||||||
element[ method ]( options );
|
|
||||||
} else if ( effectName !== method && element[ effectName ] ) {
|
|
||||||
element[ effectName ]( options.duration, options.easing, callback );
|
|
||||||
} else {
|
|
||||||
element.queue(function( next ) {
|
|
||||||
$( this )[ method ]();
|
|
||||||
if ( callback ) {
|
|
||||||
callback.call( element[ 0 ] );
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
var widget = $.widget;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
@ -14,6 +14,15 @@ License for the specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
%>
|
%>
|
||||||
<% language_tabs = current_page.data.language_tabs || [] %>
|
<% language_tabs = current_page.data.language_tabs || [] %>
|
||||||
|
<% page_content = yield %>
|
||||||
|
<%
|
||||||
|
if current_page.data.includes
|
||||||
|
current_page.data.includes.each do |include|
|
||||||
|
page_content += partial("includes/#{include}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -41,7 +50,7 @@ under the License.
|
||||||
<%= image_tag('navbar.png') %>
|
<%= image_tag('navbar.png') %>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="tocify-wrapper">
|
<div class="toc-wrapper">
|
||||||
<%= image_tag "logo.png", class: 'logo' %>
|
<%= image_tag "logo.png", class: 'logo' %>
|
||||||
<% if language_tabs.any? %>
|
<% if language_tabs.any? %>
|
||||||
<div class="lang-selector">
|
<div class="lang-selector">
|
||||||
|
|
@ -60,7 +69,21 @@ under the License.
|
||||||
</div>
|
</div>
|
||||||
<ul class="search-results"></ul>
|
<ul class="search-results"></ul>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div id="toc">
|
<div id="toc" class="toc-list-h1">
|
||||||
|
<% toc_data(page_content).each do |h1| %>
|
||||||
|
<li>
|
||||||
|
<a href="#<%= h1[:id] %>" class="toc-h1 toc-link" data-title="<%= h1[:content] %>"><%= h1[:content] %></a>
|
||||||
|
<% if h1[:children].length > 0 %>
|
||||||
|
<ul class="toc-list-h2">
|
||||||
|
<% h1[:children].each do |h2| %>
|
||||||
|
<li>
|
||||||
|
<a href="#<%= h2[:id] %>" class="toc-h2 toc-link" data-title="<%= h1[:content] %>"><%= h2[:content] %></a>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% if current_page.data.toc_footers %>
|
<% if current_page.data.toc_footers %>
|
||||||
<ul class="toc-footer">
|
<ul class="toc-footer">
|
||||||
|
|
@ -73,10 +96,7 @@ under the License.
|
||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="dark-box"></div>
|
<div class="dark-box"></div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<%= yield %>
|
<%= page_content %>
|
||||||
<% current_page.data.includes && current_page.data.includes.each do |include| %>
|
|
||||||
<%= partial "includes/#{include}" %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dark-box">
|
<div class="dark-box">
|
||||||
<% if language_tabs.any? %>
|
<% if language_tabs.any? %>
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ html, body {
|
||||||
$nav-subitem-bg;
|
$nav-subitem-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tocify-wrapper {
|
.toc-wrapper {
|
||||||
transition: left 0.3s ease-in-out;
|
transition: left 0.3s ease-in-out;
|
||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
@ -146,14 +146,6 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.tocify-item>a, .toc-footer li {
|
|
||||||
padding: 0 $nav-padding 0 $nav-padding;
|
|
||||||
display: block;
|
|
||||||
overflow-x: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Table of Contents is composed of multiple nested
|
// The Table of Contents is composed of multiple nested
|
||||||
// unordered lists. These styles remove the default
|
// unordered lists. These styles remove the default
|
||||||
// styling of an unordered list because it is ugly.
|
// styling of an unordered list because it is ugly.
|
||||||
|
|
@ -164,30 +156,17 @@ html, body {
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
|
||||||
color: $nav-text;
|
|
||||||
transition-property: background;
|
|
||||||
transition-timing-function: linear;
|
|
||||||
transition-duration: 230ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the currently selected ToC entry
|
// This is the currently selected ToC entry
|
||||||
.tocify-focus {
|
.toc-link.active {
|
||||||
box-shadow: 0px 1px 0px $nav-active-shadow;
|
box-shadow: 0px 1px 0px $nav-active-shadow;
|
||||||
background-color: $nav-active-bg;
|
background-color: $nav-active-bg;
|
||||||
color: $nav-active-text;
|
color: $nav-active-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subheaders are the submenus that slide open
|
.toc-list-h2 {
|
||||||
// in the table of contents.
|
display: none;
|
||||||
.tocify-subheader {
|
|
||||||
display: none; // tocify will override this when needed
|
|
||||||
background-color: $nav-subitem-bg;
|
background-color: $nav-subitem-bg;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
.tocify-item>a {
|
|
||||||
padding-left: $nav-padding + $nav-indent;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for embossed look:
|
// for embossed look:
|
||||||
@include embossed-bg;
|
@include embossed-bg;
|
||||||
|
|
@ -196,6 +175,11 @@ html, body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toc-h2 {
|
||||||
|
padding-left: $nav-padding + $nav-indent;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.toc-footer {
|
.toc-footer {
|
||||||
padding: 1em 0;
|
padding: 1em 0;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
|
@ -216,7 +200,19 @@ html, body {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link, .toc-footer li {
|
||||||
|
padding: 0 $nav-padding 0 $nav-padding;
|
||||||
|
display: block;
|
||||||
|
overflow-x: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
transition-property: background;
|
||||||
|
transition-timing-function: linear;
|
||||||
|
transition-duration: 130ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
// button to show navigation on mobile devices
|
// button to show navigation on mobile devices
|
||||||
|
|
@ -571,7 +567,7 @@ html, body {
|
||||||
// There are also a couple styles disperesed
|
// There are also a couple styles disperesed
|
||||||
|
|
||||||
@media (max-width: $tablet-width) {
|
@media (max-width: $tablet-width) {
|
||||||
.tocify-wrapper {
|
.toc-wrapper {
|
||||||
left: -$nav-width;
|
left: -$nav-width;
|
||||||
|
|
||||||
&.open {
|
&.open {
|
||||||
|
|
@ -587,7 +583,7 @@ html, body {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tocify-wrapper .tocify-item > a {
|
.toc-link {
|
||||||
padding-top: 0.3em;
|
padding-top: 0.3em;
|
||||||
padding-bottom: 0.3em;
|
padding-bottom: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
@ -602,7 +598,7 @@ html, body {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tocify-wrapper .lang-selector {
|
.toc-wrapper .lang-selector {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue