Code Monkey home page Code Monkey logo

sparrow's People

Contributors

cferdinandi avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

isabella232

sparrow's Issues

Fix bold search terms on search page

th,
thead td {
	// background-color: lighten( $color-gray-light, 5% );
	font-weight: bold;
	vertical-align: bottom;

	.page-search & {
		font-weight: normal;
	}

	.search-form & {
		vertical-align: top;
	}
}

More accurate .js-active class that accounts for hash values

in header/zzz_dom.js:

/**
 * Load true active state on sub nav links
 */
portalReady(function () {

	// Variables
	var links = document.querySelectorAll( '.sub ul a' );
	var url = location.protocol + '//' + location.host + location.pathname;
	var isDocsLanding = document.documentElement.classList.contains( 'dom-docs' );

	/**
	 * Add active class to link
	 */
	var activate = function ( elem ) {
		var parent = elem.parentNode;
		if (!parent) return;
		parent.className += ' js-active';
	};

	// Check if current link is active
	for ( var i = 0, len = links.length; i < len; i++ ) {

		// Get href
		var href = links[i].href;

		// Sanity check
		if ( !href ) continue;

		if ( isDocsLanding && /\/docs\/read\/Home/.test(href) ) {
			activate( links[i] );
			return;
		}

		if ( href === url.replace(/#([^\\s]*)/g, '') ) {
			activate( links[i] );
			return;
		}

	}

});

Adjust tables formatting to account for TinyMCE quirks

Don't target only th, as GUI will use tr inside thead.

/**
 * @section Tables
 * Styling for tables
 */

table {
	border-collapse: collapse;
	border-spacing: 0;
	margin-bottom: $spacing;
	max-width: 100%;
	width: 100%;
}

th,
td {
	text-align: left;
	padding: calc-em(8px);
}

thead {
	border-bottom: calc-em(2px) solid $color-gray-light;
}

th,
thead td {
	// background-color: lighten( $color-gray-light, 5% );
	font-weight: bold;
	vertical-align: bottom;
}

tbody tr {
	border-top: calc-em(1px) solid $color-gray-light;
}

td {
	vertical-align: top;
}

/**
 * Adds zebra striping
 */
.table-striped tbody tr:nth-child(odd) {
	background-color: lighten( $color-gray-light, 7% );
}


/**
 * Reduces padding on condensed tables
 */
.table-condensed th,
.table-condensed td, {
	padding: calc-em(4px);
}


/**
 * Pure CSS responsive tables
 * Adds label to each cell using the [data-label] attribute
 * @link https://techblog.livingsocial.com/blog/2015/04/06/responsive-tables-in-pure-css/
 */
@media (max-width: $bp-medium) {
	.table-responsive {

		thead {
			display: none;
			visibility: hidden;
		}

		tr {
			border-top: calc-em(1px) solid lighten( $color-gray-light, 3% );
			display: block;
			padding: calc-em(8px);
		}

		td {
			border: 0;
			display: block;
			padding: calc-em(4px);

			&:before {
				content: attr(data-label);
				display: block;
				font-weight: bold;
			}
		}

	}
}
/**
 * Add table headers that are missing from the GUI generate tables
 */
var addTableHeaders = function () {

	'use strict';

	// Feature test
	var supports = 'querySelector' in document;
	if ( !supports ) return;

	// Variables
	var tables = document.querySelectorAll( 'table' );


	/**
	 * Get the closest matching element up the DOM tree.
	 * @param  {Element} elem     Starting element
	 * @param  {String}  selector Selector to match against (class, ID, data attribute, or tag)
	 * @return {Boolean|Element}  Returns null if not match found
	 */
	var getTbody = function ( elem ) {
		for ( ; elem && elem !== document && elem.nodeType === 1; elem = elem.parentNode ) {
			if ( elem.tagName.toLowerCase() === 'tbody' ) {
				return elem;
			}
		}
		return null;
	};


	for (var i = 0; i < tables.length; i++) {

		// Check if a table head already exists
		var thead = tables[i].querySelector( 'thead' );
		if ( thead ) continue;

		// Get the first table row and conver it to a thead
		var row = tables[i].querySelector('tr');
		var tbody = getTbody( row );
		if ( !row || !tbody ) continue;
		thead = document.createElement('thead');
		thead.innerHTML = '<tr>' + row.innerHTML + '</tr>';
		console.log(tbody.parentNode);
		tbody.parentNode.insertBefore( thead, tbody );
		row.parentNode.removeChild( row );
	}

};
// Run script initializations when the Portal is ready
portalReady(function () {
    astro.init();
    clickToHighlight.init();
    fluidvids.init({
        selector: ['iframe', 'object'], // runs querySelectorAll()
        players: ['www.youtube.com', 'player.vimeo.com'] // players to support
    });
    houdini.init();
    modals.init();
    rightHeight.init();
    smoothScroll.init();
    stickyFooter.init();
    tabby.init();
    addTableHeaders();
});

WYSIWYG Generated Tables

The Mashery Portal WYSIWYG generates tables that are missing the required <thead> and <th> elements. You can fix this by calling the addTableHeaders() JavaScript method. This is done for you already in the Body JavaScript section of Portal Setup in the Dashboard.

portalReady(function () {
	addTableHeaders();
});

Full width vs. wide

/**
 * fullWidth.js
 * Copyright (c) 2017. TIBCO Software Inc. All Rights Reserved.
 * @description Make page full-width (no padding or centering)
 * @version 0.0.1
 * @author  Chris Ferdinandi
 * @param {Boolean} hideH1  If true, hide the H1 header
 * @param {Boolean} wide    If true, go wide instead of full width
 */
var fullWidth = function ( hideH1, wide ) {

	'use strict';

	// Feature test
	if ( !document.querySelector || !window.addEventListener ) return;

	// Variables
	var meta = document.querySelector( '.section-meta' );
	var edit = document.querySelector( '.section-menu .edit' );
	var h1 = document.querySelector( 'h1.first' );

	// Go full width
	if (wide) {
		document.documentElement.classList.add( 'dom-wide' );
	} else {
		document.documentElement.classList.add( 'dom-full-width' );
	}

	// Wrap elements in container class
	if ( meta ) {
		wrapElem( meta, '<div class="container">{{content}}</div>' );
	}
	if ( edit ) {
		wrapElem( edit.parentNode.parentNode, '<div class="container">{{content}}</div>' );
	}
	if ( h1 ) {

		// If enabled, hide the primary h1 element
		if ( hideH1 ) {
			h1.style.display = 'none';
			h1.style.visibility = 'hidden';
		}

		wrapElem( h1, '<div class="container">{{content}}</div>' );

	}

};

Add active parent styling hook for houdiniSubnav.js

/**
 * Houdini Subnav
 * @description  A Houdini expand-and-collapse functionality to documentation pages.
 * @version      1.0.2
 * @author       Chris Ferdinandi
 */

(function (root, factory) {
	if ( typeof define === 'function' && define.amd ) {
		define('houdiniSubnav', factory(root));
	} else if ( typeof exports === 'object' ) {
		module.exports = factory(root);
	} else {
		root.houdiniSubnav = factory(root);
	}
})(window || this, function (root) {

	'use strict';

	//
	// Variables
	//

	var houdiniSubnav = {}; // Object for public APIs
	var supports = !!document.querySelector && !!root.addEventListener; // Feature test
	var settings, theNav, navs, navOriginal;

	// Default settings
	var defaults = {
		selectorNav: '#sub > ul',
		selectorNavs: '#sub > ul > li',
		isAccordion: false,
		iconShow: '+',
		iconHide: '&ndash;',
		iconAfter: true,
		iconMargin: '0.5em',
		initClass: 'js-houdini-subnav',
	};


	//
	// Methods
	//

	/**
	 * A simple forEach() implementation for Arrays, Objects and NodeLists
	 * @private
	 * @param {Array|Object|NodeList} collection Collection of items to iterate
	 * @param {Function} callback Callback function for each iteration
	 * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
	 */
	var forEach = function (collection, callback, scope) {
		if (Object.prototype.toString.call(collection) === '[object Object]') {
			for (var prop in collection) {
				if (Object.prototype.hasOwnProperty.call(collection, prop)) {
					callback.call(scope, collection[prop], prop, collection);
				}
			}
		} else {
			for (var i = 0, len = collection.length; i < len; i++) {
				callback.call(scope, collection[i], i, collection);
			}
		}
	};

	/**
	 * Merge defaults with user options
	 * @private
	 * @param {Object} defaults Default settings
	 * @param {Object} options User options
	 * @returns {Object} Merged values of defaults and options
	 */
	var extend = function () {

		// Variables
		var extended = {};
		var deep = false;
		var i = 0;
		var length = arguments.length;

		// Check if a deep merge
		if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
			deep = arguments[0];
			i++;
		}

		// Merge the object into the extended object
		var merge = function (obj) {
			for ( var prop in obj ) {
				if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
					// If deep merge and property is an object, merge properties
					if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {
						extended[prop] = extend( true, extended[prop], obj[prop] );
					} else {
						extended[prop] = obj[prop];
					}
				}
			}
		};

		// Loop through each object and conduct a merge
		for ( ; i < length; i++ ) {
			var obj = arguments[i];
			merge(obj);
		}

		return extended;

	};

	/**
	 * Create the link node
	 * @private
	 * @param  {Node}    navlink  The current link element
	 * @param  {Boolean} isActive If true, current link is the active one
	 * @param  {Number}  index    The index for the current link in the nodeslist
	 */
	var renderLink = function ( navlink, isActive, index ) {

		// Create link
		var toggle = navlink.parentNode.querySelector( 'a[data-collapse]' ) || document.createElement( 'a' );
		var location = settings.iconAfter ? navlink.nextSibling : navlink;
		toggle.href = '#docs-subnav-' + index;
		toggle.innerHTML = '<span class="collapse-text-show">' + settings.iconShow + '</span><span class="collapse-text-hide">' + settings.iconHide + '</span>';
		toggle.classList.add( 'collapse-toggle' );
		toggle.setAttribute( 'data-collapse', true );
		if ( isActive ) {
			toggle.classList.add( 'active' );
			navlink.parentNode.classList.add( 'active-parent' );
		}
		if ( settings.isAccordion ) { toggle.setAttribute( 'data-group', 'docs-subnav' ); }

		// Add margin
		if ( settings.iconAfter ) {
			toggle.style.marginLeft = settings.iconMargin;
		} else {
			toggle.style.marginRight = settings.iconMargin;
		}

		// Inject link into DOM
		navlink.parentNode.insertBefore(toggle, location);

	};

	/**
	 * Loop through each subnav element and add expand-and-collapse attributes
	 * @private
	 * @param {NodesList} navs Nav elements
	 */
	var addAttributes = function ( navs, offset ) {
		offset = offset ? offset : '';
		forEach(navs, function (nav, index) {

			// Get subnav
			var subnav = nav.querySelector( 'ul' );

			// If no subnav, move on to the next nav element
			if ( !subnav ) return;

			// Get subnav link
			var navlink = nav.firstChild;

			// Determine if nav is active
			var isActive = nav.classList.contains( 'active' );

			// Remove .active class from parent li
			nav.classList.remove( 'active' );

			// Render the link
			renderLink( navlink, isActive, offset + '-' + index );

			// Add classes and ID to subnav
			subnav.classList.add( 'collapse' );
			subnav.id = 'docs-subnav-' + offset + '-' + index;
			if ( isActive ) { subnav.classList.add( 'active' ); }

			// If subnav has subnav, run again
			var subSubNavs = subnav.children;
			addAttributes( subSubNavs, offset + '-' + index );

		});
	};

	/**
	 * Destroy the current initialization.
	 * @public
	 */
	houdiniSubnav.destroy = function () {

		if ( !settings ) return;

		// Remove init class
		document.documentElement.classList.remove( settings.initClass );

		// Restore original nav
		theNav.innerHTML = navOriginal;

		// Reset variables
		settings = null;
		theNav = null;
		navs = null;

	};

	/**
	 * Initialize Houdini
	 * @public
	 * @param {Object} options User settings
	 */
	houdiniSubnav.init = function ( options ) {

		// feature test
		if ( !supports ) return;

		// Destroy any existing initializations
		houdiniSubnav.destroy();

		// Variables
		settings = extend( defaults, options || {} ); // Merge user options with defaults
		theNav = document.querySelector( settings.selectorNav );
		navs = document.querySelectorAll( settings.selectorNavs );

		// If set to only run on Docs and not docs page, end
		if ( !document.body.classList.contains( 'page-docs' ) ) return;

		// Add class to HTML element to activate conditional CSS
		document.documentElement.classList.add( settings.initClass );

		// Save original nav
		navOriginal = theNav.innerHTML;

		// Add Houdini hooks to subnav
		addAttributes( navs );

	};


	//
	// Public APIs
	//

	return houdiniSubnav;

});

Add form expand/toggle option

Add a callback

/**
 * conditionalContactFields.js
 * @description Show or hide contact form fields based on the response to one question
 * @version 0.0.1
 * @author  Chris Ferdinandi
 */
var conditionalContactFields = function ( question, answers ) {

    'use strict';

    // Sanity check
    if ( !question || !answers || Object.prototype.toString.call(answers) !== '[object Object]' ) return;

    // Feature test
    if ( !document.querySelector || !window.addEventListener ) return;


    //
    // Variables
    //


    //
    // Methods
    //

    /**
     * A simple forEach() implementation for Arrays, Objects and NodeLists.
     * @private
     * @author Todd Motto
     * @link   https://github.com/toddmotto/foreach
     * @param {Array|Object|NodeList} collection Collection of items to iterate
     * @param {Function}              callback   Callback function for each iteration
     * @param {Array|Object|NodeList} scope      Object/NodeList/Array that forEach is iterating over (aka `this`)
     */
    var forEach = function ( collection, callback, scope ) {
        if ( Object.prototype.toString.call( collection ) === '[object Object]' ) {
            for ( var prop in collection ) {
                if ( Object.prototype.hasOwnProperty.call( collection, prop ) ) {
                    callback.call( scope, collection[prop], prop, collection );
                }
            }
        } else {
            for ( var i = 0 ; i < collection.length; i++ ) {
                callback.call( scope, collection[i], i, collection );
            }
        }
    };

    /**
     * Get the closest matching element up the DOM tree.
     * @private
     * @param  {Element} elem     Starting element
     * @param  {String}  selector Selector to match against (class, ID, data attribute, or tag)
     * @return {Boolean|Element}  Returns null if not match found
     */
    var getClosest = function ( elem, selector ) {

        // Variables
        var firstChar = selector.charAt(0);
        var attribute, value;

        // If selector is a data attribute, split attribute from value
        if ( firstChar === '[' ) {
            selector = selector.substr(1, selector.length - 2);
            attribute = selector.split( '=' );

            if ( attribute.length > 1 ) {
                value = true;
                attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' );
            }
        }

        // Get closest match
        for ( ; elem && elem !== document; elem = elem.parentNode ) {

            // If selector is a class
            if ( firstChar === '.' ) {
                if ( elem.classList.contains( selector.substr(1) ) ) {
                    return elem;
                }
            }

            // If selector is an ID
            if ( firstChar === '#' ) {
                if ( elem.id === selector.substr(1) ) {
                    return elem;
                }
            }

            // If selector is a data attribute
            if ( firstChar === '[' ) {
                if ( elem.hasAttribute( attribute[0] ) ) {
                    if ( value ) {
                        if ( elem.getAttribute( attribute[0] ) === attribute[1] ) {
                            return elem;
                        }
                    } else {
                        return elem;
                    }
                }
            }

            // If selector is a tag
            if ( elem.tagName.toLowerCase() === selector ) {
                return elem;
            }

        }

        return null;

    };

    var hideField = function ( field ) {
        if ( !field ) return;
        field.style.display = 'none';
        field.style.visibility = 'hidden';
        if ( !field.value || field.value === ''  ) {
            field.value = 'null';
        }
    };

    var showField = function ( field ) {
        if ( !field ) return;
        field.style.display = 'block';
        field.style.visibility = 'visible';
        if ( field.value === 'null' ) {
            field.value = '';
        }
    };

    var hideFields = function ( chosen ) {
        forEach(answers, function (fields, answer) {

            // Don't hide the select answer fields
            if ( chosen.toString() === answer.toString() ) return;

            // Sanity check
            if ( Object.prototype.toString.call(fields) !== '[object Array]' ) return;

            // Hide the fields
            forEach(fields, function (selector) {
                hideField( document.querySelector( '[for="' + selector + '"]' ) );
                hideField( document.querySelector( '#' + selector ) );
            });

        });
    };

    var showFields = function ( chosen ) {

        // Get the fields
        var fields = answers[chosen];

        // Sanity check
        if ( !fields ) return;
        if ( Object.prototype.toString.call(fields) !== '[object Array]' ) return;

        // Hide the fields
        forEach(fields, function (selector) {
            showField( document.querySelector( '[for="' + selector + '"]' ) );
            showField( document.querySelector( '#' + selector ) );
        });

    };

    /**
     * Handle toggle click events
     * @private
     */
    var eventHandler = function (event) {
        var toggle = getClosest( event.target, question );
        if ( !toggle ) return;
        hideFields( toggle.value );
        showFields( toggle.value );
    };



    //
    // Event Listeners and inits
    //

    // Get the question selector
    var selector = document.querySelector( question );
    if ( !selector ) return;

    // Show/hide fields on load
    hideFields( selector.value );

    // Listen for click events
    document.addEventListener('click', eventHandler, false);

};

Fix breadcrumbs.js

/**
 * breadcrumbs.js
 * @description Add breadcrumbs to the Portal
 * @version     1.0.1
 * @author      Chris Ferdinandi
 */
(function (root, factory) {
	if ( typeof define === 'function' && define.amd ) {
		define('breadcrumbs', factory(root));
	} else if ( typeof exports === 'object' ) {
		module.exports = factory(root);
	} else {
		root.breadcrumbs = factory(root);
	}
})(window || this, function (root) {

	'use strict';

	//
	// Variables
	//

	var breadcrumbs = {}; // Object for public APIs
	var supports = !!document.querySelector; // Feature test
	var settings, path;

	// Default settings
	var defaults = {
		pages: 'all', // all | docs | page | blog | forum | ioDocs | account
		exclude: '.dom-landing',
		selector: '#page',
		docs: 'Documentation',
		ioDocs: 'IO-Docs',
		forumAddCategory: 'Add Category',
		accountMyAccount: 'My Account',
		accountMyKeys: 'My API Keys',
		accountMyApps: 'My Applications',
		memberManage: 'Manage My Account',
		memberEmail: 'Change Email',
		memberPassword: 'Change Password',
		customTitles: {},
		initClass: 'js-breadcrumbs',
		containerClass: 'container padding-top-small text-muted link-plain',
		listClass: 'list-inline list-inline-breadcrumbs',
		callback: function () {},
	};


	//
	// Methods
	//

	/**
	 * Convert string to title case
	 * @private
	 * @param  {String} str The string to convert to title case
	 * @return {String}     Title cased string
	 * @url http://stackoverflow.com/a/4878800/1293256
	 * @todo  If IO-Docs, custom override
	 */
	var toTitleCase = function  ( str ) {

		// Variables
		var titleCase;

		// Otherwise, transform to title case
		titleCase = str.replace( /\w\S*/g,
			function ( txt ) {
				return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
			});

		// Manual overrides for certain pages
		if ( titleCase === 'Docs' ) titleCase = settings.docs; // Documentation
		if ( titleCase === 'Io-docs' ) titleCase = settings.ioDocs; // IO-Docs
		if ( titleCase === 'Category-add' ) titleCase = settings.forumAddCategory; // New Forum Category
		if ( titleCase === 'Mykeys' ) titleCase = settings.accountMyKeys; // My API Keys
		if ( titleCase === 'Myapps' ) titleCase = settings.accountMyApps; // My Applications
		if ( document.body.id === 'page-member' && titleCase === 'Edit' ) titleCase = settings.memberManage; // Manage Account
		if ( document.body.id === 'page-member' && titleCase === 'Email' ) titleCase = settings.memberEmail; // Change email
		if ( document.body.id === 'page-member' && titleCase === 'Passwd' ) titleCase = settings.memberPassword; // Change password
		if ( settings.customTitles.hasOwnProperty( titleCase.toLowerCase() ) ) titleCase = settings.customTitles[titleCase.toLowerCase()]; // Custom overrides

		// @todo If forum and not "add category", return empty after 'forum'

		return titleCase;

	};

	/**
	 * Remove junk from the path and set variables
	 * @private
	 * @param  {String} path The URL path
	 * @return {[type]}      [description]
	 */
	var sanitizePath = function () {

		// If Docs or Blog, remove /read and /Home
		if ( document.body.classList.contains( 'page-docs' ) || document.body.classList.contains( 'page-blog' ) ) {
			path = path.replace( /\/read/, '' ).replace( /\/Home/, '' );
		}

		// If Forum, remove /read
		if ( document.body.classList.contains( 'page-forum' ) ) {
			path = path.replace( /\/read/, '' );
		}

		// If Profile, remove redundant /profile
		if ( document.body.classList.contains( 'page-profile' ) ) {
			path = path.substring( path.indexOf( '/profile' ), 8 );
		}

		// Remove leading and trailing slashes
		if ( path.charAt(0) === '/' ) { path = path.substr(1); }
		if ( path.charAt( path.length - 1 ) === '/' ) { path = path.substr(0, path.length - 1); }

	};

	/**
	 * Create breadcrumb markup
	 * @private
	 * @return {String} The breadcrumb markup
	 */
	var createBreadcrumbs = function () {

		// Variables
		var crumbs = path.split( '/' );
		var count = crumbs.length;
		var breadcrumbs = '<li><a href="/">Home</a></li>';
		var isForum = document.body.classList.contains( 'page-forum' );
		var link = '';

		// If it's the landing page, remove link from "Home" breadcrumb
		if ( document.documentElement.classList.contains( 'dom-landing' ) ) breadcrumbs = '<li>Home</li>';

		// Create breadcrumb links
		buoy.forEach(crumbs, function (crumb, index) {

			// If crumb is empty or it's the forum, bail
			if ( crumb === '' || isForum ) return;

			// Create the title
			var title = toTitleCase( crumb.replace( /_/g, ' ' ) );

			// If the last crumb, don't add a link
			if ( index === count - 1 ) {
				breadcrumbs += '<li>' + title + '</li>';
				return;
			}

			// If is account/member page, override App/Member crumb
			if ( document.body.classList.contains( 'page-apps' ) || document.body.classList.contains( 'page-member' ) ) {
				if ( title === 'Apps' || title === 'Member'  ) {
					breadcrumbs += '<li><a href="/apps/mykeys">' + settings.accountMyAccount + '</a></li>';
					return;
				}
			}

			// Otherwise, create a linked crumb
			link += '/' + crumb;
			console.log(link);
			breadcrumbs += '<li><a href="' + link + '">' + title + '</a></li>';

		});

		// Custom breadcrumb for the forum
		if ( isForum ) {
			if ( document.documentElement.classList.contains( 'dom-forum' ) ) {
				breadcrumbs += '<li>Forum</li>';
			} else {
				breadcrumbs += '<li><a href="/forum">Forum</a></li>';
			}
		}

		return '<div class="' + settings.containerClass + '" id="nav-breadcrumbs"><ul class="' + settings.listClass + '" id="nav-breadcrumbs-list">' + breadcrumbs + '</ul></div>';

	};

	/**
	 * Load breadcrumbs into the DOM
	 * @private
	 */
	var loadCrumbs = function () {

		// Remove junk from the path
		sanitizePath();

		// Create breadcrumbs
		var breadcrumbs = createBreadcrumbs();

		// Inject breadcrumbs into the DOM
		loadHTML( breadcrumbs, document.querySelector( settings.selector ) );

		// Run callback
		settings.callback();

	};

	/**
	 * Check to see if the page is on the exclude/include list
	 * @private
	 * @return {Boolean} Returns true if the breadcrumbs should be loaded onto the page
	 */
	var okToRun = function () {

		// Variables
		var selectors = '';

		// Don't run on explicitly excluded pages
		if ( settings.exclude && document.querySelector( settings.exclude ) ) return false;

		// Don't run on the wiki page
		if ( document.body.classList.contains( 'page-wiki' ) ) return false;

		// Create selector(s)
		if ( Object.prototype.toString.call( settings.pages ) === '[object Array]' ) {

			// Get number of pages
			var count = settings.pages.length;

			// For each one, create a selector
			buoy.forEach(settings.pages, function (page, index) {

				// Add a comma delimiter to all but the last item
				var delimiter = index + 1 === count ? '' : ', ';

				// Special dual class for account pages
				if ( page === 'account' ) {
					selectors += '.page-account, .page-member';
					return;
				}

				// Create selector
				selectors += '.page-' + page + delimiter;

			});
		} else {

			// If it should be run on all pages, return true
			if ( settings.pages === 'all' ) return true;

			// Create selector
			selectors = '.page-' + settings.pages;

		}

		// If selectors exist, OK to run.
		// Otherwise, return false.
		var pages = document.querySelectorAll( selectors );
		if ( pages.length === 0 ) return false;
		return true;

	};

	/**
	 * Destroy the current initialization.
	 * @public
	 */
	breadcrumbs.destroy = function () {

		if ( !settings ) return;

		// Remove init class
		document.documentElement.classList.remove( settings.initClass );

		// Remove breadcrumbs from the DOM
		var breadcrumbs = document.querySelector( '#nav-breadcrumbs' );
		if ( breadcrumbs ) {
			breadcrumbs.parentNode.removeChild( breadcrumbs );
		}

		// Reset variables
		settings = null;
		path = null;

	};

	/**
	 * Initialize Houdini
	 * @public
	 * @param {Object} options User settings
	 */
	breadcrumbs.init = function ( options ) {

		// feature test
		if ( !supports ) return;

		// Destroy any existing initializations
		breadcrumbs.destroy();

		// Merge user options with defaults
		settings = buoy.extend( true, defaults, options || {} );

		// Check if it's ok to run based on user settings
		if ( !okToRun() ) return;

		// Get the current URL path
		path = window.location.pathname;

		// Add class to HTML element to activate conditional CSS
		document.documentElement.classList.add( settings.initClass );

		// Load breadcrumbs onto the page
		loadCrumbs();

	};


	//
	// Public APIs
	//

	return breadcrumbs;

});

Update SVG support script

;(function (window, document, undefined) {

	'use strict';

	// SVG feature detection
	var supports = !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;

	// If SVG is supported, add `.svg` class to <html> element
	if ( !supports ) return;
	document.documentElement.className += (document.documentElement.className ? ' ' : '') + 'svg';

})(window, document);

Fix table layout on Search

thead {
	border-bottom: calc-em(2px) solid $color-gray-light;

	.search-form & {
		border-bottom: 0;
	}
}

th,
thead td {
	// background-color: lighten( $color-gray-light, 5% );
	font-weight: bold;
	vertical-align: bottom;

	.search-form & {
		vertical-align: top;
	}
}

Fix stickyFooter init bug

In body.js:

// Run script initializations when the Portal is ready
portalReady(function () {
    astro.init();
    clickToHighlight.init();
    fluidvids.init({
        selector: ['iframe', 'object'], // runs querySelectorAll()
        players: ['www.youtube.com', 'player.vimeo.com'] // players to support
    });
    houdini.init({
        selectorToggle: '.collapse-toggle',
    });
    rightHeight.init({
        selector: '.js-right-height',
        selectorContent: '.js-right-height-content',
    });
    smoothScroll.init({
        selector: '.js-scroll',
        selectorHeader: '.js-scroll-header',
    });
    stickyFooter.init({
        selector: '.js-sticky-footer',
    });
    tabby.init({
        selectorToggle: '.js-tab',
        selectorToggleGroup: '.tabs',
        selectorContent: '.tabs-pane',
        selectorContentGroup: '.js-tabs-content',
    });
    addTableHeaders();
});

Fix min-width/max-width conflict

// Calculate pixels from ems
@function calc-px($em, $base: 16) {
	$em: strip-unit($em);
	$base: strip-unit($base);
	@if $em == 1px {
		@return 1px;
	}
	@return ($em * $base) * 1px;
}

@media (max-width: (calc-em(calc-px($bp-large) - 1))) { ... }

Update subnav houdini margins

/**
 * Houdini Subnav
 * @description  A Houdini expand-and-collapse functionality to documentation pages.
 * @version      1.0.0
 * @author       Chris Ferdinandi
 */

(function (root, factory) {
    if ( typeof define === 'function' && define.amd ) {
        define('houdiniSubnav', factory(root));
    } else if ( typeof exports === 'object' ) {
        module.exports = factory(root);
    } else {
        root.houdiniSubnav = factory(root);
    }
})(window || this, function (root) {

    'use strict';

    //
    // Variables
    //

    var houdiniSubnav = {}; // Object for public APIs
    var supports = !!document.querySelector && !!root.addEventListener; // Feature test
    var settings, theNav, navs, navOriginal;

    // Default settings
    var defaults = {
        selectorNav: '#sub > ul',
        selectorNavs: '#sub > ul > li',
        isAccordion: false,
        iconShow: '+',
        iconHide: '&ndash;',
        iconAfter: true,
        iconMargin: '0.5em',
        initClass: 'js-houdini-subnav',
    };


    //
    // Methods
    //

    /**
     * Create the link node
     * @private
     * @param  {Node}    navlink  The current link element
     * @param  {Boolean} isActive If true, current link is the active one
     * @param  {Number}  index    The index for the current link in the nodeslist
     */
    var renderLink = function ( navlink, isActive, index ) {

        // Create link
        var toggle = document.createElement( 'a' );
        var location = settings.iconAfter ? navlink.nextSibling : navlink;
        toggle.href = '#docs-subnav-' + index;
        toggle.innerHTML = '<span class="collapse-text-show">' + settings.iconShow + '</span><span class="collapse-text-hide">' + settings.iconHide + '</span>';
        toggle.classList.add( 'collapse-toggle' );
        toggle.setAttribute( 'data-collapse', true );
        if ( settings.iconAfter ) {
            toggle.style.marginLeft = settings.iconMargin;
        } else {
            toggle.style.marginRight = settings.iconMargin;
        }
        if ( isActive ) { toggle.classList.add( 'active' ); }
        if ( settings.isAccordion ) { toggle.setAttribute( 'data-group', 'docs-subnav' ); }

        // Inject link into DOM
        navlink.parentNode.insertBefore(toggle, location);

    };

    /**
     * Loop through each subnav element and add expand-and-collapse attributes
     * @private
     * @param {NodesList} navs Nav elements
     */
    var addAttributes = function ( navs ) {
        buoy.forEach(navs, function (nav, index) {

            // Get subnav
            var subnav = nav.querySelector( 'ul' );

            // If no subnav, move on to the next nav element
            if ( !subnav ) return;

            // Get subnav link
            var navlink = nav.firstChild;

            // Determine if nav is active
            var isActive = nav.classList.contains( 'active' );

            // Remove .active class from parent li
            nav.classList.remove( 'active' );

            // Render the link
            renderLink( navlink, isActive, index );

            // Add classes and ID to subnav
            subnav.classList.add( 'collapse' );
            subnav.id = 'docs-subnav-' + index;
            if ( isActive ) { subnav.classList.add( 'active' ); }

            // If subnav has subnav, run again
            var subSubNavs = subnav.querySelectorAll( 'ul > li' );
            addAttributes( subSubNavs );

        });
    };

    /**
     * Destroy the current initialization.
     * @public
     */
    houdiniSubnav.destroy = function () {

        if ( !settings ) return;

        // Remove init class
        document.documentElement.classList.remove( settings.initClass );

        // Restore original nav
        theNav.innerHTML = navOriginal;

        // Reset variables
        settings = null;
        theNav = null;
        navs = null;

    };

    /**
     * Initialize Houdini
     * @public
     * @param {Object} options User settings
     */
    houdiniSubnav.init = function ( options ) {

        // feature test
        if ( !supports ) return;

        // Destroy any existing initializations
        houdiniSubnav.destroy();

        // Variables
        settings = buoy.extend( defaults, options || {} ); // Merge user options with defaults
        theNav = document.querySelector( settings.selectorNav );
        navs = document.querySelectorAll( settings.selectorNavs );

        // If set to only run on Docs and not docs page, end
        if ( !document.body.classList.contains( 'page-docs' ) ) return;

        // Add class to HTML element to activate conditional CSS
        document.documentElement.classList.add( settings.initClass );

        // Save original nav
        navOriginal = theNav.innerHTML;

        // Add Houdini hooks to subnav
        addAttributes( navs );

    };


    //
    // Public APIs
    //

    return houdiniSubnav;

});

Fix auto-table headers

  • use (labels[n].innerText || labels[n].textContent)
  • Account for thead/tbody to avoid some quirks

Adjust approach to font loading to avoid FOUT on reload

loadCSS('https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,700');
if ( docCookies.getItem( 'fontsLoaded' ) ) { 
	document.documentElement.className += ' fonts-loaded'; 
} else {
	var font = new FontFaceObserver('Source Sans Pro');
	font.load().then(function () {
		var expires = new Date(+new Date() + (7 * 24 * 60 * 60 * 1000)).toUTCString();
		document.cookie = 'fontsLoaded=true; expires=' + expires;
		document.documentElement.className += ' fonts-loaded';
	});
}

And cookies.js in detects.js:

/*\
|*|
|*|  :: cookies.js ::
|*|
|*|  A complete cookies reader/writer framework with full unicode support.
|*|
|*|  Revision #1 - September 4, 2014
|*|
|*|  https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
|*|  https://developer.mozilla.org/User:fusionchess
|*|  https://github.com/madmurphy/cookies.js
|*|
|*|  This framework is released under the GNU Public License, version 3 or later.
|*|  http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
|*|  Syntaxes:
|*|
|*|  * docCookies.setItem(name, value[, end[, path[, domain[, secure]]]])
|*|  * docCookies.getItem(name)
|*|  * docCookies.removeItem(name[, path[, domain]])
|*|  * docCookies.hasItem(name)
|*|  * docCookies.keys()
|*|
\*/

var docCookies = {
  getItem: function (sKey) {
    if (!sKey) { return null; }
    return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
  },
  // setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
  //   if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
  //   var sExpires = "";
  //   if (vEnd) {
  //     switch (vEnd.constructor) {
  //       case Number:
  //         sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
  //         break;
  //       case String:
  //         sExpires = "; expires=" + vEnd;
  //         break;
  //       case Date:
  //         sExpires = "; expires=" + vEnd.toUTCString();
  //         break;
  //     }
  //   }
  //   document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
  //   return true;
  // },
  // removeItem: function (sKey, sPath, sDomain) {
  //   if (!this.hasItem(sKey)) { return false; }
  //   document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "");
  //   return true;
  // },
  // hasItem: function (sKey) {
  //   if (!sKey) { return false; }
  //   return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
  // },
  // keys: function () {
  //   var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
  //   for (var nLen = aKeys.length, nIdx = 0; nIdx < nLen; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); }
  //   return aKeys;
  // }
};

Fix houdini on subnav

/**
 * Houdini subnav
 */
.js-houdini ul.collapse.active {
	margin-left: $spacing;
}

Update houdiniSubnav.js for nesting

/**
 * Houdini Subnav
 * @description  A Houdini expand-and-collapse functionality to documentation pages.
 * @version      1.0.1
 * @author       Chris Ferdinandi
 */

(function (root, factory) {
	if ( typeof define === 'function' && define.amd ) {
		define('houdiniSubnav', factory(root));
	} else if ( typeof exports === 'object' ) {
		module.exports = factory(root);
	} else {
		root.houdiniSubnav = factory(root);
	}
})(window || this, function (root) {

	'use strict';

	//
	// Variables
	//

	var houdiniSubnav = {}; // Object for public APIs
	var supports = !!document.querySelector && !!root.addEventListener; // Feature test
	var settings, theNav, navs, navOriginal;

	// Default settings
	var defaults = {
		selectorNav: '#sub > ul',
		selectorNavs: '#sub > ul > li',
		isAccordion: false,
		iconShow: '+',
		iconHide: '&ndash;',
		iconAfter: true,
		iconMargin: '0.5em',
		initClass: 'js-houdini-subnav',
	};


	//
	// Methods
	//

	/**
	 * A simple forEach() implementation for Arrays, Objects and NodeLists
	 * @private
	 * @param {Array|Object|NodeList} collection Collection of items to iterate
	 * @param {Function} callback Callback function for each iteration
	 * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
	 */
	var forEach = function (collection, callback, scope) {
		if (Object.prototype.toString.call(collection) === '[object Object]') {
			for (var prop in collection) {
				if (Object.prototype.hasOwnProperty.call(collection, prop)) {
					callback.call(scope, collection[prop], prop, collection);
				}
			}
		} else {
			for (var i = 0, len = collection.length; i < len; i++) {
				callback.call(scope, collection[i], i, collection);
			}
		}
	};

	/**
	 * Merge defaults with user options
	 * @private
	 * @param {Object} defaults Default settings
	 * @param {Object} options User options
	 * @returns {Object} Merged values of defaults and options
	 */
	var extend = function () {

		// Variables
		var extended = {};
		var deep = false;
		var i = 0;
		var length = arguments.length;

		// Check if a deep merge
		if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
			deep = arguments[0];
			i++;
		}

		// Merge the object into the extended object
		var merge = function (obj) {
			for ( var prop in obj ) {
				if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
					// If deep merge and property is an object, merge properties
					if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {
						extended[prop] = extend( true, extended[prop], obj[prop] );
					} else {
						extended[prop] = obj[prop];
					}
				}
			}
		};

		// Loop through each object and conduct a merge
		for ( ; i < length; i++ ) {
			var obj = arguments[i];
			merge(obj);
		}

		return extended;

	};

	/**
	 * Create the link node
	 * @private
	 * @param  {Node}    navlink  The current link element
	 * @param  {Boolean} isActive If true, current link is the active one
	 * @param  {Number}  index    The index for the current link in the nodeslist
	 */
	var renderLink = function ( navlink, isActive, index ) {

		// Create link
		var toggle = navlink.parentNode.querySelector( 'a[data-collapse]' ) || document.createElement( 'a' );
		var location = settings.iconAfter ? navlink.nextSibling : navlink;
		toggle.href = '#docs-subnav-' + index;
		toggle.innerHTML = '<span class="collapse-text-show">' + settings.iconShow + '</span><span class="collapse-text-hide">' + settings.iconHide + '</span>';
		toggle.classList.add( 'collapse-toggle' );
		toggle.setAttribute( 'data-collapse', true );
		if ( isActive ) { toggle.classList.add( 'active' ); }
		if ( settings.isAccordion ) { toggle.setAttribute( 'data-group', 'docs-subnav' ); }

		// Add margin
		if ( settings.iconAfter ) {
			toggle.style.marginLeft = settings.iconMargin;
		} else {
			toggle.style.marginRight = settings.iconMargin;
		}

		// Inject link into DOM
		navlink.parentNode.insertBefore(toggle, location);

	};

	/**
	 * Loop through each subnav element and add expand-and-collapse attributes
	 * @private
	 * @param {NodesList} navs Nav elements
	 */
	var addAttributes = function ( navs, offset ) {
		offset = offset ? offset : '';
		forEach(navs, function (nav, index) {

			// Get subnav
			var subnav = nav.querySelector( 'ul' );

			// If no subnav, move on to the next nav element
			if ( !subnav ) return;

			// Get subnav link
			var navlink = nav.firstChild;

			// Determine if nav is active
			var isActive = nav.classList.contains( 'active' );

			// Remove .active class from parent li
			nav.classList.remove( 'active' );

			// Render the link
			renderLink( navlink, isActive, offset + '-' + index );

			// Add classes and ID to subnav
			subnav.classList.add( 'collapse' );
			subnav.id = 'docs-subnav-' + offset + '-' + index;
			if ( isActive ) { subnav.classList.add( 'active' ); }

			// If subnav has subnav, run again
			var subSubNavs = subnav.children;
			addAttributes( subSubNavs, offset + '-' + index );

		});
	};

	/**
	 * Destroy the current initialization.
	 * @public
	 */
	houdiniSubnav.destroy = function () {

		if ( !settings ) return;

		// Remove init class
		document.documentElement.classList.remove( settings.initClass );

		// Restore original nav
		theNav.innerHTML = navOriginal;

		// Reset variables
		settings = null;
		theNav = null;
		navs = null;

	};

	/**
	 * Initialize Houdini
	 * @public
	 * @param {Object} options User settings
	 */
	houdiniSubnav.init = function ( options ) {

		// feature test
		if ( !supports ) return;

		// Destroy any existing initializations
		houdiniSubnav.destroy();

		// Variables
		settings = extend( defaults, options || {} ); // Merge user options with defaults
		theNav = document.querySelector( settings.selectorNav );
		navs = document.querySelectorAll( settings.selectorNavs );

		// If set to only run on Docs and not docs page, end
		if ( !document.body.classList.contains( 'page-docs' ) ) return;

		// Add class to HTML element to activate conditional CSS
		document.documentElement.classList.add( settings.initClass );

		// Save original nav
		navOriginal = theNav.innerHTML;

		// Add Houdini hooks to subnav
		addAttributes( navs );

	};


	//
	// Public APIs
	//

	return houdiniSubnav;

});

Switch to BSD license

Copyright (c) 2017, TIBCO Software Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

The name of TIBCO Software Inc. may not be used to endorse or promote products derived from this software without specific prior written permission of TIBCO Software Inc.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Create script to adjust page heading/title on any page

Targets:

  • h1.first - H1 heading on most pages
  • #sub > h2 - Docs subnav
  • #main > h1 - IO Docs, My Account, Signed Out Confirm, (prefix with page selector)
  • .dom-login-login h2.sign-in - Sign In

Add specific page targets, too

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.