UNPKG

refapp

Version:

Parse Refract JSON to API Reference Jquery SPA

300 lines (272 loc) 8.91 kB
/** * refapp * @author E-Com Club <ti@e-com.club> * @license MIT */ (function ($) { 'use strict' // require 'partials/consume-refract.js' /* global consumeRefract */ // setup as jQuery plugin $.fn.refapp = function (refracts, Options) { // main DOM element var $app = this // default options object var options = { // styles asideClasses: '', articleClasses: '', // base URL hash baseHash: '/', // parse Markdown to HTML mdParser: function (md) { return md }, // optional callback function for loaded refracts refractCallback: null, // callback function for endoint actions actionCallback: function (req, res) { console.log(req, res) } } if (Options) { Object.assign(options, Options) } // random base ID for elements var elId = Math.floor(Math.random() * (9999 - 1000)) + 1000 // create DOM elements // main app Components var $article = $('<article>', { 'class': options.articleClasses }) var $list = $('<div>', { 'class': 'list-group my-3 mr-md-5 pr-lg-3 pr-xl-5 ref-resources' }) var $resources = [] var $ol = $('<ol>', { 'class': 'ref-anchors' }) var $aside = $('<aside>', { 'class': options.asideClasses, html: [ '<h5>Summary</h5>', $ol, '<h5>Reference</h5>', $list ] }) // console.log(this) // console.log(refract) // current resource anchor var baseHash = options.baseHash var currentAnchor, waitingHash $(window).on('hashchange', function () { if (currentAnchor && !(new RegExp('^#' + currentAnchor).test(location.hash))) { // resource changed // try to route route() } }) var route = function () { var hash = location.hash if (hash) { if (hash.slice(baseHash.length + 1).indexOf('/') === -1) { // should have at least one bar window.location.hash = hash + '/' return route() } // test refract fragment route for (var i = 0; i < $resources.length; i++) { var $link = $resources[i] if (new RegExp('^#' + $link.data('anchor')).test(hash)) { // found if (!$link.hasClass('active')) { // save current hash for further update waitingHash = hash // start routing $link.click() } return true } } // rewrite Apiary default hashes var parts = hash.match(/^#(reference|introduction)\/(.*)$/) if (parts) { window.location.hash = '#' + baseHash + parts[2] // route again return route() } } // not routed return false } // get each refract fragment if (Array.isArray(refracts)) { var firstRefract = true var processRefract = function (Refract, anchor) { // reset DOM $ol.slideUp(200, function () { $(this).html('') // fade article content $article.fadeOut(200, function () { $(this).html('') // start treating Refract JSON (Drafter output) // API Elements format /* Reference https://github.com/apiaryio/drafter https://api-elements.readthedocs.io/en/latest/ */ // consume refract tree var refract = Object.assign({}, Refract) while (refract) { // root API Element fixed refract = consumeRefract(refract, anchor, options, $article, $ol) /* if (!options.apiTitle) { // try to set API title options.apiTitle = apiElementMeta(refract, 'title') } */ } // show content again $article.fadeIn('slow', function () { if (firstRefract) { firstRefract = false } else { // scroll to content $('html, body').animate({ scrollTop: $article.offset().top - 20 }, 'slow') } if (typeof options.refractCallback === 'function') { // send refract object options.refractCallback(Refract) } }) $ol.slideDown('slow', function () { if (waitingHash) { if (waitingHash !== location.hash) { var $link = $(this).find('a[href="' + waitingHash + '"]') if ($link.length) { setTimeout(function () { // need to call native DOM click() // https://stackoverflow.com/questions/34174134 $link[0].click() }, 100) } } // reset waitingHash = null } }) // set links to new browser tab $article.find('a').filter(function () { var attr = $(this).attr('href') return (attr.charAt(0) !== '#' && attr !== 'javascript:;') }).attr('target', '_blank') }) }) } var requestFailed = function (jqxhr, textStatus, err) { // AJAX error alert('Cannot GET Refract JSON: ' + textStatus) console.error(err) } var getRefract = function (i, anchor) { // try to GET JSON file var url = refracts[i].src if (typeof url === 'string' && url !== '') { $.getJSON(url, function (data) { processRefract(data, anchor) }) .fail(requestFailed) } else { console.error(new Error('Invalid or undefined src string on refract (' + i + '), ignored')) } } // list all fragments for (var i = 0; i < refracts.length; i++) { if (typeof refracts[i] === 'object' && refracts[i] !== null) { var title = refracts[i].title if (title) { title = typeof title === 'object' ? title.content : title if (typeof title === 'string' && title.trim() !== '') { // generate anchor for this recfract fragment var anchor = baseHash + title.toLowerCase().replace(/\s/g, '-') + '/' $resources.push($('<a>', { 'class': 'list-group-item list-group-item-action', href: 'javascript:;', text: title, 'data-anchor': anchor, click: (function (i, anchor) { // local vars return function () { // clear last active $list.find('a.active').removeClass('active') $(this).addClass('active') // update content getRefract(i, anchor) // scroll to top $('html, body').animate({ scrollTop: 0 }, 'slow', 'swing', function () { // update current anchor currentAnchor = anchor // update URL hash window.location.hash = '#' + anchor }) } }(i, anchor)) })) } } // add resources to list DOM $list.append($resources) } } } // create collapsable elements for navs var divId = 'ref-anchors-' + elId $aside.addClass('collapse d-md-block').attr('id', divId) var $sidebar = [ $('<a>', { 'class': 'btn btn-xl btn-outline-primary btn-block d-md-none mb-3', 'data-toggle': 'collapse', 'aria-expanded': 'false', 'aria-control': divId, href: '#' + divId, role: 'button', html: '<i class="ti-angle-down mr-1"></i> Content' }), $aside ] // Reference App body HTML var body = [] // optional API title if (options.apiTitle) { body.push($('<h1>', { 'class': 'mt-3 mb-4 text-muted', text: options.apiTitle })) } body.push($article) // update DOM $app.html($('<div>', { 'class': 'container', // compose Reference App layout html: $('<div>', { 'class': 'row', html: [ $('<div>', { 'class': 'col-md-5 col-xl-4 ref-sidebar', html: [ $('<section>', { 'class': 'py-4 sticky-top', html: $sidebar }) ] }), $('<div>', { 'class': 'col-md-7 col-xl-8 ref-body', html: body }) ] }) })) // first route if (!route()) { // start with the first refract fragment $resources[0].click() } } }(jQuery))