require.config({ paths: { bootstrap: "./vendor/bootstrap.min", diffMatchPatch: "./vendor/diff_match_patch.min", handlebars: "./vendor/handlebars.min", handlebarsExtended: "./utils/handlebars_helper", jquery: "./vendor/jquery.min", locales: "./locales/locale", lodash: "./vendor/lodash.custom.min", pathToRegexp: "./vendor/path-to-regexp/index", prismjs: "./vendor/prism", semver: "./vendor/semver.min", utilsSampleRequest: "./utils/send_sample_request", webfontloader: "./vendor/webfontloader", list: "./vendor/list.min", apiData: "./api_data", apiProject: "./api_project" }, shim: { bootstrap: { deps: ["jquery"] }, diffMatchPatch: { exports: "diff_match_patch" }, handlebars: { exports: "Handlebars" }, handlebarsExtended: { deps: ["jquery", "handlebars"], exports: "Handlebars" }, prismjs: { exports: "Prism" } }, urlArgs: "v=" + new Date().getTime(), waitSeconds: 150 }); require([ "jquery", "lodash", "locales", "handlebarsExtended", "apiProject", "apiData", "prismjs", "utilsSampleRequest", "semver", "webfontloader", "bootstrap", "pathToRegexp", "list" ], function( $, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver, WebFont ) { // Load google web fonts. WebFont.load({ active: function() { // Only init after fonts are loaded. init( $, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver ); }, inactive: function() { // Run init, even if loading fonts fails init( $, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver ); }, google: { families: ["Source Code Pro", "Source Sans Pro:n4,n6,n7"] } }); }); function init( $, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver ) { var api = apiData.api; // // Templates // var templateHeader = Handlebars.compile($("#template-header").html()); var templateFooter = Handlebars.compile($("#template-footer").html()); var templateArticle = Handlebars.compile($("#template-article").html()); var templateCompareArticle = Handlebars.compile( $("#template-compare-article").html() ); var templateGenerator = Handlebars.compile($("#template-generator").html()); var templateProject = Handlebars.compile($("#template-project").html()); var templateSections = Handlebars.compile($("#template-sections").html()); var templateSidenav = Handlebars.compile($("#template-sidenav").html()); // // Default host url used if no sampleUrl is present in config // var baseURL = window.location.origin; // // apiProject defaults // if (!apiProject.template) apiProject.template = {}; if (apiProject.template.withCompare == null) apiProject.template.withCompare = true; if (apiProject.template.withGenerator == null) apiProject.template.withGenerator = true; if (apiProject.template.forceLanguage) locale.setLanguage(apiProject.template.forceLanguage); if (apiProject.template.aloneDisplay == null) apiProject.template.aloneDisplay = false; // Setup jQuery Ajax $.ajaxSetup(apiProject.template.jQueryAjaxSetup); // // Data transform // // grouped by group var apiByGroup = _.groupBy(api, function(entry) { return entry.group; }); // grouped by group and name var apiByGroupAndName = {}; $.each(apiByGroup, function(index, entries) { apiByGroupAndName[index] = _.groupBy(entries, function(entry) { return entry.name; }); }); // // sort api within a group by title ASC and custom order // var newList = []; var umlauts = { ä: "ae", ü: "ue", ö: "oe", ß: "ss" }; // TODO: remove in version 1.0 $.each(apiByGroupAndName, function(index, groupEntries) { // get titles from the first entry of group[].name[] (name has versioning) var titles = []; $.each(groupEntries, function(titleName, entries) { var title = entries[0].title; if (title !== undefined) { title.toLowerCase().replace(/[äöüß]/g, function($0) { return umlauts[$0]; }); titles.push(title + "#~#" + titleName); // '#~#' keep reference to titleName after sorting } }); // sort by name ASC titles.sort(); // custom order if (apiProject.order) titles = sortByOrder(titles, apiProject.order, "#~#"); // add single elements to the new list titles.forEach(function(name) { var values = name.split("#~#"); var key = values[1]; groupEntries[key].forEach(function(entry) { newList.push(entry); }); }); }); // api overwrite with ordered list api = newList; // // Group- and Versionlists // var apiGroups = {}; var apiGroupTitles = {}; var apiVersions = {}; apiVersions[apiProject.version] = 1; $.each(api, function(index, entry) { apiGroups[entry.group] = 1; apiGroupTitles[entry.group] = entry.groupTitle || entry.group; apiVersions[entry.version] = 1; }); // sort groups apiGroups = Object.keys(apiGroups); apiGroups.sort(); // custom order if (apiProject.order) apiGroups = sortByOrder(apiGroups, apiProject.order); // sort versions DESC apiVersions = Object.keys(apiVersions); apiVersions.sort(semver.compare); apiVersions.reverse(); // // create Navigationlist // var nav = []; apiGroups.forEach(function(group) { // Mainmenu entry nav.push({ group: group, isHeader: true, title: apiGroupTitles[group] }); // Submenu var oldName = ""; api.forEach(function(entry) { if (entry.group === group) { if (oldName !== entry.name) { nav.push({ title: entry.title, group: group, name: entry.name, type: entry.type, version: entry.version, url: entry.url }); } else { nav.push({ title: entry.title, group: group, hidden: true, name: entry.name, type: entry.type, version: entry.version, url: entry.url }); } oldName = entry.name; } }); }); /** * Add navigation items by analyzing the HTML content and searching for h1 and h2 tags * @param nav Object the navigation array * @param content string the compiled HTML content * @param index where to insert items * @return boolean true if any good-looking (i.e. with a group identifier)

tag was found */ function add_nav(nav, content, index) { var found_level1 = false; if (!content) { return found_level1; } var topics = content.match(/(.+?)<\/h(1|2)>/gi); if (topics) { topics.forEach(function(entry) { var level = entry.substring(2, 3); var title = entry.replace(/<.+?>/g, ""); // Remove all HTML tags for the title var entry_tags = entry.match(/id="api-([^\-]+)(?:-(.+))?"/); // Find the group and name in the id property var group = entry_tags ? entry_tags[1] : null; var name = entry_tags ? entry_tags[2] : null; if (level == 1 && title && group) { nav.splice(index, 0, { group: group, isHeader: true, title: title, isFixed: true }); index++; found_level1 = true; } if (level == 2 && title && group && name) { nav.splice(index, 0, { group: group, name: name, isHeader: false, title: title, isFixed: false, version: "1.0" }); index++; } }); } return found_level1; } // Mainmenu Header entry if (apiProject.header) { var found_level1 = add_nav(nav, apiProject.header.content, 0); // Add level 1 and 2 titles if (!found_level1) { // If no Level 1 tags were found, make a title nav.unshift({ group: "_", isHeader: true, title: apiProject.header.title == null ? locale.__("General") : apiProject.header.title, isFixed: true }); } } // Mainmenu Footer entry if (apiProject.footer) { var last_nav_index = nav.length; var found_level1 = add_nav(nav, apiProject.footer.content, nav.length); // Add level 1 and 2 titles if (!found_level1 && apiProject.footer.title != null) { // If no Level 1 tags were found, make a title nav.splice(last_nav_index, 0, { group: "_footer", isHeader: true, title: apiProject.footer.title, isFixed: true }); } } // render pagetitle var title = apiProject.title ? apiProject.title : "apiDoc: " + apiProject.name + " - " + apiProject.version; $(document).attr("title", title); // remove loader $("#loader").remove(); // render sidenav var fields = { nav: nav }; $("#sidenav").append(templateSidenav(fields)); // render Generator $("#generator").append(templateGenerator(apiProject)); // render Project _.extend(apiProject, { versions: apiVersions }); $("#project").append(templateProject(apiProject)); // render apiDoc, header/footer documentation if (apiProject.header) $("#header").append(templateHeader(apiProject.header)); if (apiProject.footer) $("#footer").append(templateFooter(apiProject.footer)); // // Render Sections and Articles // var articleVersions = {}; var content = ""; apiGroups.forEach(function(groupEntry) { var articles = []; var oldName = ""; var fields = {}; var title = groupEntry; var description = ""; articleVersions[groupEntry] = {}; // render all articles of a group api.forEach(function(entry) { if (groupEntry === entry.group) { if (oldName !== entry.name) { // determine versions api.forEach(function(versionEntry) { if ( groupEntry === versionEntry.group && entry.name === versionEntry.name ) { if (!articleVersions[entry.group].hasOwnProperty(entry.name)) { articleVersions[entry.group][entry.name] = []; } articleVersions[entry.group][entry.name].push( versionEntry.version ); } }); fields = { article: entry, versions: articleVersions[entry.group][entry.name] }; } else { fields = { article: entry, hidden: true, versions: articleVersions[entry.group][entry.name] }; } if (apiProject.sampleUrl == false) { fields.article.sampleRequest = [ { url: baseURL + fields.article.url } ]; } // add prefix URL for endpoint unless it's already absolute if (apiProject.url) { if (fields.article.url.substr(0, 4).toLowerCase() !== "http") { fields.article.url = apiProject.url + fields.article.url; } } addArticleSettings(fields, entry); if (entry.groupTitle) title = entry.groupTitle; // TODO: make groupDescription compareable with older versions (not important for the moment) if (entry.groupDescription) description = entry.groupDescription; articles.push({ article: templateArticle(fields), group: entry.group, name: entry.name, aloneDisplay: apiProject.template.aloneDisplay }); oldName = entry.name; } }); // render Section with Articles var fields = { group: groupEntry, title: title, description: description, articles: articles, aloneDisplay: apiProject.template.aloneDisplay }; content += templateSections(fields); }); $("#sections").append(content); // Bootstrap Scrollspy $(this).scrollspy({ target: "#scrollingNav" }); // Content-Scroll on Navigation click. $(".sidenav") .find("a") .on("click", function(e) { e.preventDefault(); var id = $(this).attr("href"); if ($(id).length > 0) $("html,body").animate( { scrollTop: parseInt($(id).offset().top) }, 400 ); window.location.hash = $(this).attr("href"); }); /** * Check if Parameter (sub) List has a type Field. * Example: @apiSuccess varname1 No type. * @apiSuccess {String} varname2 With type. * * @param {Object} fields */ function _hasTypeInFields(fields) { var result = false; $.each(fields, function(name) { result = result || _.some(fields[name], function(item) { return item.type; }); }); return result; } /** * On Template changes, recall plugins. */ function initDynamic() { // Bootstrap popover $('button[data-toggle="popover"]') .popover() .click(function(e) { e.preventDefault(); }); var version = $("#version strong").html(); $("#sidenav li").removeClass("is-new"); if (apiProject.template.withCompare) { $("#sidenav li[data-version='" + version + "']").each(function() { var group = $(this).data("group"); var name = $(this).data("name"); var length = $( "#sidenav li[data-group='" + group + "'][data-name='" + name + "']" ).length; var index = $( "#sidenav li[data-group='" + group + "'][data-name='" + name + "']" ).index($(this)); if (length === 1 || index === length - 1) $(this).addClass("is-new"); }); } // tabs $(".nav-tabs-examples a").click(function(e) { e.preventDefault(); $(this).tab("show"); }); $(".nav-tabs-examples") .find("a:first") .tab("show"); // sample header-content-type switch $(".sample-header-content-type-switch").change(function() { var paramName = "." + $(this).attr("name") + "-fields"; var bodyName = "." + $(this).attr("name") + "-body"; var selectName = "select[name=" + $(this).attr("name") + "]"; if ($(this).val() == "body-json") { $(selectName).val("undefined"); $(this).val("body-json"); $(paramName).removeClass("hide"); $(this) .parent() .nextAll(paramName) .first() .addClass("hide"); $(bodyName).addClass("hide"); $(this) .parent() .nextAll(bodyName) .first() .removeClass("hide"); } else if ($(this).val() == "body-form-data") { $(selectName).val("undefined"); $(this).val("body-form-data"); $(bodyName).addClass("hide"); $(paramName).removeClass("hide"); } else { $(this) .parent() .nextAll(paramName) .first() .removeClass("hide"); $(this) .parent() .nextAll(bodyName) .first() .addClass("hide"); } $(this) .prev(".sample-request-switch") .prop("checked", true); }); // sample request switch $(".sample-request-switch").click(function(e) { var paramName = "." + $(this).attr("name") + "-fields"; var bodyName = "." + $(this).attr("name") + "-body"; var select = $(this) .next("." + $(this).attr("name") + "-select") .val(); if ($(this).prop("checked")) { if (select == "body-json") { $(this) .parent() .nextAll(bodyName) .first() .removeClass("hide"); } else { $(this) .parent() .nextAll(paramName) .first() .removeClass("hide"); } } else { if (select == "body-json") { $(this) .parent() .nextAll(bodyName) .first() .addClass("hide"); } else { $(this) .parent() .nextAll(paramName) .first() .addClass("hide"); } } }); if (apiProject.template.aloneDisplay) { //show group $(".show-group").click(function() { var apiGroup = "." + $(this).attr("data-group") + "-group"; var apiGroupArticle = "." + $(this).attr("data-group") + "-article"; $(".show-api-group").addClass("hide"); $(apiGroup).removeClass("hide"); $(".show-api-article").addClass("hide"); $(apiGroupArticle).removeClass("hide"); }); //show api $(".show-api").click(function() { var apiName = "." + $(this).attr("data-name") + "-article"; var apiGroup = "." + $(this).attr("data-group") + "-group"; $(".show-api-group").addClass("hide"); $(apiGroup).removeClass("hide"); $(".show-api-article").addClass("hide"); $(apiName).removeClass("hide"); }); } // call scrollspy refresh method $(window).scrollspy("refresh"); // init modules sampleRequest.initDynamic(); Prism.highlightAll(); } initDynamic(); if (apiProject.template.aloneDisplay) { var hashVal = window.location.hash; if (hashVal != null && hashVal.length !== 0) { $("." + hashVal.slice(1) + "-init").click(); } } // // HTML-Template specific jQuery-Functions // // Change Main Version function setMainVersion(selectedVersion) { if (typeof selectedVersion === "undefined") { selectedVersion = $("#version strong").html(); } else { $("#version strong").html(selectedVersion); } // hide all $("article").addClass("hide"); $("#sidenav li:not(.nav-fixed)").addClass("hide"); // show 1st equal or lower Version of each entry $("article[data-version]").each(function(index) { var group = $(this).data("group"); var name = $(this).data("name"); var version = $(this).data("version"); if (semver.lte(version, selectedVersion)) { if ( $( "article[data-group='" + group + "'][data-name='" + name + "']:visible" ).length === 0 ) { // enable Article $( "article[data-group='" + group + "'][data-name='" + name + "'][data-version='" + version + "']" ).removeClass("hide"); // enable Navigation $( "#sidenav li[data-group='" + group + "'][data-name='" + name + "'][data-version='" + version + "']" ).removeClass("hide"); $("#sidenav li.nav-header[data-group='" + group + "']").removeClass( "hide" ); } } }); // show 1st equal or lower Version of each entry $("article[data-version]").each(function(index) { var group = $(this).data("group"); $("section#api-" + group).removeClass("hide"); if ($("section#api-" + group + " article:visible").length === 0) { $("section#api-" + group).addClass("hide"); } else { $("section#api-" + group).removeClass("hide"); } }); initDynamic(); return; } setMainVersion(); $("#versions li.version a").on("click", function(e) { e.preventDefault(); setMainVersion($(this).html()); }); // compare all article with their predecessor $("#compareAllWithPredecessor").on("click", changeAllVersionCompareTo); // change version of an article $("article .versions li.version a").on("click", changeVersionCompareTo); // compare url-parameter $.urlParam = function(name) { var results = new RegExp("[\\?&]" + name + "=([^&#]*)").exec( window.location.href ); return results && results[1] ? results[1] : null; }; if ($.urlParam("compare")) { // URL Paramter ?compare=1 is set $("#compareAllWithPredecessor").trigger("click"); } // Quick jump on page load to hash position. // Should happen after setting the main version // and after triggering the click on the compare button, // as these actions modify the content // and would make it jump to the wrong position or not jump at all. if (window.location.hash) { var id = decodeURI(window.location.hash); if ($(id).length > 0) $("html,body").animate({ scrollTop: parseInt($(id).offset().top) }, 0); } /** * Initialize search */ var options = { valueNames: ["nav-list-item", "nav-list-url-item"] }; var endpointsList = new List("scrollingNav", options); /** * Set initial focus to search input */ $("#scrollingNav .sidenav-search input.search").focus(); /** * Detect ESC key to reset search */ $(document).keyup(function(e) { if (e.keyCode === 27) $("span.search-reset").click(); }); /** * Search reset */ $("span.search-reset").on("click", function() { $("#scrollingNav .sidenav-search input.search") .val("") .focus(); endpointsList.search(); }); /** * Change version of an article to compare it to an other version. */ function changeVersionCompareTo(e) { e.preventDefault(); var $root = $(this).parents("article"); var selectedVersion = $(this).html(); var $button = $root.find(".version"); var currentVersion = $button.find("strong").html(); $button.find("strong").html(selectedVersion); var group = $root.data("group"); var name = $root.data("name"); var version = $root.data("version"); var compareVersion = $root.data("compare-version"); if (compareVersion === selectedVersion) return; if (!compareVersion && version == selectedVersion) return; if ( (compareVersion && articleVersions[group][name][0] === selectedVersion) || version === selectedVersion ) { // the version of the entry is set to the highest version (reset) resetArticle(group, name, version); } else { var $compareToArticle = $( "article[data-group='" + group + "'][data-name='" + name + "'][data-version='" + selectedVersion + "']" ); var sourceEntry = {}; var compareEntry = {}; $.each(apiByGroupAndName[group][name], function(index, entry) { if (entry.version === version) sourceEntry = entry; if (entry.version === selectedVersion) compareEntry = entry; }); var fields = { article: sourceEntry, compare: compareEntry, versions: articleVersions[group][name] }; // add unique id // TODO: replace all group-name-version in template with id. fields.article.id = fields.article.group + "-" + fields.article.name + "-" + fields.article.version; fields.article.id = fields.article.id.replace(/\./g, "_"); fields.compare.id = fields.compare.group + "-" + fields.compare.name + "-" + fields.compare.version; fields.compare.id = fields.compare.id.replace(/\./g, "_"); var entry = sourceEntry; if (entry.parameter && entry.parameter.fields) fields._hasTypeInParameterFields = _hasTypeInFields( entry.parameter.fields ); if (entry.error && entry.error.fields) fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields); if (entry.success && entry.success.fields) fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields); if (entry.info && entry.info.fields) fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields); var entry = compareEntry; if ( fields._hasTypeInParameterFields !== true && entry.parameter && entry.parameter.fields ) fields._hasTypeInParameterFields = _hasTypeInFields( entry.parameter.fields ); if ( fields._hasTypeInErrorFields !== true && entry.error && entry.error.fields ) fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields); if ( fields._hasTypeInSuccessFields !== true && entry.success && entry.success.fields ) fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields); if ( fields._hasTypeInInfoFields !== true && entry.info && entry.info.fields ) fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields); var content = templateCompareArticle(fields); $root.after(content); var $content = $root.next(); // Event on.click re-assign $content .find(".versions li.version a") .on("click", changeVersionCompareTo); // select navigation $( "#sidenav li[data-group='" + group + "'][data-name='" + name + "'][data-version='" + currentVersion + "']" ).addClass("has-modifications"); $root.remove(); // TODO: on change main version or select the highest version re-render } initDynamic(); } /** * Compare all currently selected Versions with their predecessor. */ function changeAllVersionCompareTo(e) { e.preventDefault(); $("article:visible .versions").each(function() { var $root = $(this).parents("article"); var currentVersion = $root.data("version"); var $foundElement = null; $(this) .find("li.version a") .each(function() { var selectVersion = $(this).html(); if (selectVersion < currentVersion && !$foundElement) $foundElement = $(this); }); if ($foundElement) $foundElement.trigger("click"); }); initDynamic(); } /** * Sort the fields. */ function sortFields(fields_object) { $.each(fields_object, function(key, fields) { // Find only object fields var objects = fields.filter(function(item) { return item.type === "Object"; }); // Check if has any object if (objects.length === 0) { return; } // Iterate over all objects for (var object of objects) { // Retrieve the index var index = fields.indexOf(object); // Find all child fields for this object var objectFields = fields.filter(function(item) { return item.field.indexOf(object.field + ".") > -1; }); // Get the child index var firstIndex = fields.indexOf(objectFields[0]); // Put the object it before the first child index fields.splice(index, 1); fields.splice(firstIndex, 0, object); // Startup the last index with the object index var lastIndex = firstIndex; // Iterate over all children for (var child of objectFields) { lastIndex++; // Retrieve the index var childIndex = fields.indexOf(child); // Put it after the object declaration fields.splice(childIndex, 1); fields.splice(lastIndex, 0, child); } } // Retrieve the first object field index var firstObjectIndex = fields.indexOf(objects[0]); // Find all non-object fields that doesn't contain dot notation var nonObjects = fields.filter(function(item) { return item.field.indexOf(".") === -1 && item.type !== "Object"; }); // Iterate over all non-objects for (var nonObject of nonObjects) { // Put it before the first object field var index = fields.indexOf(nonObject); fields.splice(index, 1); fields.splice(firstObjectIndex, 0, nonObject); } }); } /** * Add article settings. */ function addArticleSettings(fields, entry) { // add unique id // TODO: replace all group-name-version in template with id. fields.id = fields.article.group + "-" + fields.article.name + "-" + fields.article.version; fields.id = fields.id.replace(/\./g, "_"); if (entry.header && entry.header.fields) { sortFields(entry.header.fields); fields._hasTypeInHeaderFields = _hasTypeInFields(entry.header.fields); } if (entry.parameter && entry.parameter.fields) { sortFields(entry.parameter.fields); fields._hasTypeInParameterFields = _hasTypeInFields( entry.parameter.fields ); } if (entry.error && entry.error.fields) { sortFields(entry.error.fields); fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields); } if (entry.success && entry.success.fields) { sortFields(entry.success.fields); fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields); } if (entry.info && entry.info.fields) { sortFields(entry.info.fields); fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields); } // add template settings fields.template = apiProject.template; } /** * Render Article. */ function renderArticle(group, name, version) { var entry = {}; $.each(apiByGroupAndName[group][name], function(index, currentEntry) { if (currentEntry.version === version) entry = currentEntry; }); var fields = { article: entry, versions: articleVersions[group][name] }; addArticleSettings(fields, entry); return templateArticle(fields); } /** * Render original Article and remove the current visible Article. */ function resetArticle(group, name, version) { var $root = $( "article[data-group='" + group + "'][data-name='" + name + "']:visible" ); var content = renderArticle(group, name, version); $root.after(content); var $content = $root.next(); // Event on.click needs to be reassigned (should actually work with on ... automatically) $content.find(".versions li.version a").on("click", changeVersionCompareTo); $( "#sidenav li[data-group='" + group + "'][data-name='" + name + "'][data-version='" + version + "']" ).removeClass("has-modifications"); $root.remove(); return; } /** * Return ordered entries by custom order and append not defined entries to the end. * @param {String[]} elements * @param {String[]} order * @param {String} splitBy * @return {String[]} Custom ordered list. */ function sortByOrder(elements, order, splitBy) { var results = []; order.forEach(function(name) { if (splitBy) elements.forEach(function(element) { var parts = element.split(splitBy); var key = parts[0]; // reference keep for sorting if (key == name || parts[1] == name) results.push(element); }); else elements.forEach(function(key) { if (key == name) results.push(name); }); }); // Append all other entries that ar not defined in order elements.forEach(function(element) { if (results.indexOf(element) === -1) results.push(element); }); return results; } Prism.highlightAll(); }