3 bootstrap: './vendor/bootstrap.min',
4 diffMatchPatch: './vendor/diff_match_patch.min',
5 handlebars: './vendor/handlebars.min',
6 handlebarsExtended: './utils/handlebars_helper',
7 jquery: './vendor/jquery.min',
8 locales: './locales/locale',
9 lodash: './vendor/lodash.custom.min',
10 pathToRegexp: './vendor/path-to-regexp/index',
11 prettify: './vendor/prettify/prettify',
12 semver: './vendor/semver.min',
13 utilsSampleRequest: './utils/send_sample_request',
14 webfontloader: './vendor/webfontloader',
15 list: './vendor/list.min'
22 exports: 'diff_match_patch'
28 deps: ['jquery', 'handlebars'],
32 exports: 'prettyPrint'
35 urlArgs: 'v=' + (new Date()).getTime(),
53 ], function($, _, locale, Handlebars, apiProject, apiData, prettyPrint, sampleRequest, semver, WebFont) {
55 // load google web fonts
58 var api = apiData.api;
63 var templateHeader = Handlebars.compile( $('#template-header').html() );
64 var templateFooter = Handlebars.compile( $('#template-footer').html() );
65 var templateArticle = Handlebars.compile( $('#template-article').html() );
66 var templateCompareArticle = Handlebars.compile( $('#template-compare-article').html() );
67 var templateGenerator = Handlebars.compile( $('#template-generator').html() );
68 var templateProject = Handlebars.compile( $('#template-project').html() );
69 var templateSections = Handlebars.compile( $('#template-sections').html() );
70 var templateSidenav = Handlebars.compile( $('#template-sidenav').html() );
73 // apiProject defaults
75 if ( ! apiProject.template)
76 apiProject.template = {};
78 if (apiProject.template.withCompare == null)
79 apiProject.template.withCompare = true;
81 if (apiProject.template.withGenerator == null)
82 apiProject.template.withGenerator = true;
84 if (apiProject.template.forceLanguage)
85 locale.setLanguage(apiProject.template.forceLanguage);
88 $.ajaxSetup(apiProject.template.jQueryAjaxSetup);
94 var apiByGroup = _.groupBy(api, function(entry) {
98 // grouped by group and name
99 var apiByGroupAndName = {};
100 $.each(apiByGroup, function(index, entries) {
101 apiByGroupAndName[index] = _.groupBy(entries, function(entry) {
107 // sort api within a group by title ASC and custom order
110 var umlauts = { 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // TODO: remove in version 1.0
111 $.each (apiByGroupAndName, function(index, groupEntries) {
112 // get titles from the first entry of group[].name[] (name has versioning)
114 $.each (groupEntries, function(titleName, entries) {
115 var title = entries[0].title;
116 if(title !== undefined) {
117 title.toLowerCase().replace(/[äöüß]/g, function($0) { return umlauts[$0]; });
118 titles.push(title + '#~#' + titleName); // '#~#' keep reference to titleName after sorting
125 if (apiProject.order)
126 titles = sortByOrder(titles, apiProject.order, '#~#');
128 // add single elements to the new list
129 titles.forEach(function(name) {
130 var values = name.split('#~#');
132 groupEntries[key].forEach(function(entry) {
137 // api overwrite with ordered list
141 // Group- and Versionlists
144 var apiGroupTitles = {};
145 var apiVersions = {};
146 apiVersions[apiProject.version] = 1;
148 $.each(api, function(index, entry) {
149 apiGroups[entry.group] = 1;
150 apiGroupTitles[entry.group] = entry.groupTitle || entry.group;
151 apiVersions[entry.version] = 1;
155 apiGroups = Object.keys(apiGroups);
159 if (apiProject.order)
160 apiGroups = sortByOrder(apiGroups, apiProject.order);
162 // sort versions DESC
163 apiVersions = Object.keys(apiVersions);
164 apiVersions.sort(semver.compare);
165 apiVersions.reverse();
168 // create Navigationlist
171 apiGroups.forEach(function(group) {
176 title: apiGroupTitles[group]
181 api.forEach(function(entry) {
182 if (entry.group === group) {
183 if (oldName !== entry.name) {
189 version: entry.version
198 version: entry.version
201 oldName = entry.name;
207 * Add navigation items by analyzing the HTML content and searching for h1 and h2 tags
208 * @param nav Object the navigation array
209 * @param content string the compiled HTML content
210 * @param index where to insert items
211 * @return boolean true if any good-looking (i.e. with a group identifier) <h1> tag was found
213 function add_nav(nav, content, index) {
214 var found_level1 = false;
218 var topics = content.match(/<h(1|2).*?>(.+?)<\/h(1|2)>/gi);
220 topics.forEach(function(entry) {
221 var level = entry.substring(2,3);
222 var title = entry.replace(/<.+?>/g, ''); // Remove all HTML tags for the title
223 var entry_tags = entry.match(/id="api-([^\-]+)(?:-(.+))?"/); // Find the group and name in the id property
224 var group = (entry_tags ? entry_tags[1] : null);
225 var name = (entry_tags ? entry_tags[2] : null);
226 if (level==1 && title && group) {
227 nav.splice(index, 0, {
236 if (level==2 && title && group && name) {
237 nav.splice(index, 0, {
252 // Mainmenu Header entry
253 if (apiProject.header) {
254 var found_level1 = add_nav(nav, apiProject.header.content, 0); // Add level 1 and 2 titles
255 if (!found_level1) { // If no Level 1 tags were found, make a title
259 title: (apiProject.header.title == null) ? locale.__('General') : apiProject.header.title,
265 // Mainmenu Footer entry
266 if (apiProject.footer) {
267 var last_nav_index = nav.length;
268 var found_level1 = add_nav(nav, apiProject.footer.content, nav.length); // Add level 1 and 2 titles
269 if (!found_level1 && apiProject.footer.title != null) { // If no Level 1 tags were found, make a title
270 nav.splice(last_nav_index, 0, {
273 title: apiProject.footer.title,
280 var title = apiProject.title ? apiProject.title : 'apiDoc: ' + apiProject.name + ' - ' + apiProject.version;
281 $(document).attr('title', title);
284 $('#loader').remove();
290 $('#sidenav').append( templateSidenav(fields) );
293 $('#generator').append( templateGenerator(apiProject) );
296 _.extend(apiProject, { versions: apiVersions});
297 $('#project').append( templateProject(apiProject) );
299 // render apiDoc, header/footer documentation
300 if (apiProject.header)
301 $('#header').append( templateHeader(apiProject.header) );
303 if (apiProject.footer)
304 $('#footer').append( templateFooter(apiProject.footer) );
307 // Render Sections and Articles
309 var articleVersions = {};
311 apiGroups.forEach(function(groupEntry) {
315 var title = groupEntry;
316 var description = '';
317 articleVersions[groupEntry] = {};
319 // render all articles of a group
320 api.forEach(function(entry) {
321 if(groupEntry === entry.group) {
322 if (oldName !== entry.name) {
323 // determine versions
324 api.forEach(function(versionEntry) {
325 if (groupEntry === versionEntry.group && entry.name === versionEntry.name) {
326 if ( ! articleVersions[entry.group].hasOwnProperty(entry.name) ) {
327 articleVersions[entry.group][entry.name] = [];
329 articleVersions[entry.group][entry.name].push(versionEntry.version);
334 versions: articleVersions[entry.group][entry.name]
340 versions: articleVersions[entry.group][entry.name]
344 // add prefix URL for endpoint
346 fields.article.url = apiProject.url + fields.article.url;
348 addArticleSettings(fields, entry);
350 if (entry.groupTitle)
351 title = entry.groupTitle;
353 // TODO: make groupDescription compareable with older versions (not important for the moment)
354 if (entry.groupDescription)
355 description = entry.groupDescription;
358 article: templateArticle(fields),
362 oldName = entry.name;
366 // render Section with Articles
370 description: description,
373 content += templateSections(fields);
375 $('#sections').append( content );
377 // Bootstrap Scrollspy
378 $(this).scrollspy({ target: '#scrollingNav', offset: 18 });
380 // Content-Scroll on Navigation click.
381 $('.sidenav').find('a').on('click', function(e) {
383 var id = $(this).attr('href');
384 if ($(id).length > 0)
385 $('html,body').animate({ scrollTop: parseInt($(id).offset().top) }, 400);
386 window.location.hash = $(this).attr('href');
389 // Quickjump on Pageload to hash position.
390 if(window.location.hash) {
391 var id = window.location.hash;
392 if ($(id).length > 0)
393 $('html,body').animate({ scrollTop: parseInt($(id).offset().top) }, 0);
397 * Check if Parameter (sub) List has a type Field.
398 * Example: @apiSuccess varname1 No type.
399 * @apiSuccess {String} varname2 With type.
401 * @param {Object} fields
403 function _hasTypeInFields(fields) {
405 $.each(fields, function(name) {
406 result = result || _.some(fields[name], function(item) { return item.type; });
412 * On Template changes, recall plugins.
414 function initDynamic() {
416 $('button[data-toggle="popover"]').popover().click(function(e) {
420 var version = $('#version strong').html();
421 $('#sidenav li').removeClass('is-new');
422 if (apiProject.template.withCompare) {
423 $('#sidenav li[data-version=\'' + version + '\']').each(function(){
424 var group = $(this).data('group');
425 var name = $(this).data('name');
426 var length = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').length;
427 var index = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').index($(this));
428 if (length === 1 || index === (length - 1))
429 $(this).addClass('is-new');
434 $('.nav-tabs-examples a').click(function (e) {
438 $('.nav-tabs-examples').find('a:first').tab('show');
440 // sample request switch
441 $('.sample-request-switch').click(function (e) {
442 var name = '.' + $(this).attr('name') + '-fields';
443 $(name).addClass('hide');
444 $(this).parent().next(name).removeClass('hide');
447 // call scrollspy refresh method
448 $(window).scrollspy('refresh');
451 sampleRequest.initDynamic();
455 // Pre- / Code-Format
459 // HTML-Template specific jQuery-Functions
461 // Change Main Version
462 $('#versions li.version a').on('click', function(e) {
465 var selectedVersion = $(this).html();
466 $('#version strong').html(selectedVersion);
469 $('article').addClass('hide');
470 $('#sidenav li:not(.nav-fixed)').addClass('hide');
472 // show 1st equal or lower Version of each entry
473 $('article[data-version]').each(function(index) {
474 var group = $(this).data('group');
475 var name = $(this).data('name');
476 var version = $(this).data('version');
478 if (semver.lte(version, selectedVersion)) {
479 if ($('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible').length === 0) {
481 $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
483 $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
484 $('#sidenav li.nav-header[data-group=\'' + group + '\']').removeClass('hide');
489 // show 1st equal or lower Version of each entry
490 $('article[data-version]').each(function(index) {
491 var group = $(this).data('group');
492 $('section#api-' + group).removeClass('hide');
493 if ($('section#api-' + group + ' article:visible').length === 0) {
494 $('section#api-' + group).addClass('hide');
496 $('section#api-' + group).removeClass('hide');
504 // compare all article with their predecessor
505 $('#compareAllWithPredecessor').on('click', changeAllVersionCompareTo);
507 // change version of an article
508 $('article .versions li.version a').on('click', changeVersionCompareTo);
510 // compare url-parameter
511 $.urlParam = function(name) {
512 var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
513 return (results && results[1]) ? results[1] : null;
516 if ($.urlParam('compare')) {
517 // URL Paramter ?compare=1 is set
518 $('#compareAllWithPredecessor').trigger('click');
520 if (window.location.hash) {
521 var id = window.location.hash;
522 $('html,body').animate({ scrollTop: parseInt($(id).offset().top) - 18 }, 0);
530 valueNames: [ 'nav-list-item' ]
532 var endpointsList = new List('scrollingNav', options);
535 * Set initial focus to search input
537 $('#scrollingNav .sidenav-search input.search').focus();
540 * Detect ESC key to reset search
542 $(document).keyup(function(e) {
543 if (e.keyCode === 27) $('span.search-reset').click();
549 $('span.search-reset').on('click', function() {
550 $('#scrollingNav .sidenav-search input.search')
554 endpointsList.search();
558 * Change version of an article to compare it to an other version.
560 function changeVersionCompareTo(e) {
563 var $root = $(this).parents('article');
564 var selectedVersion = $(this).html();
565 var $button = $root.find('.version');
566 var currentVersion = $button.find('strong').html();
567 $button.find('strong').html(selectedVersion);
569 var group = $root.data('group');
570 var name = $root.data('name');
571 var version = $root.data('version');
573 var compareVersion = $root.data('compare-version');
575 if (compareVersion === selectedVersion)
578 if ( ! compareVersion && version == selectedVersion)
581 if (compareVersion && articleVersions[group][name][0] === selectedVersion || version === selectedVersion) {
582 // the version of the entry is set to the highest version (reset)
583 resetArticle(group, name, version);
585 var $compareToArticle = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + selectedVersion + '\']');
587 var sourceEntry = {};
588 var compareEntry = {};
589 $.each(apiByGroupAndName[group][name], function(index, entry) {
590 if (entry.version === version)
592 if (entry.version === selectedVersion)
593 compareEntry = entry;
597 article: sourceEntry,
598 compare: compareEntry,
599 versions: articleVersions[group][name]
603 // TODO: replace all group-name-version in template with id.
604 fields.article.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
605 fields.article.id = fields.article.id.replace(/\./g, '_');
607 fields.compare.id = fields.compare.group + '-' + fields.compare.name + '-' + fields.compare.version;
608 fields.compare.id = fields.compare.id.replace(/\./g, '_');
610 var entry = sourceEntry;
611 if (entry.parameter && entry.parameter.fields)
612 fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
614 if (entry.error && entry.error.fields)
615 fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
617 if (entry.success && entry.success.fields)
618 fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
620 if (entry.info && entry.info.fields)
621 fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
623 var entry = compareEntry;
624 if (fields._hasTypeInParameterFields !== true && entry.parameter && entry.parameter.fields)
625 fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
627 if (fields._hasTypeInErrorFields !== true && entry.error && entry.error.fields)
628 fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
630 if (fields._hasTypeInSuccessFields !== true && entry.success && entry.success.fields)
631 fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
633 if (fields._hasTypeInInfoFields !== true && entry.info && entry.info.fields)
634 fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
636 var content = templateCompareArticle(fields);
637 $root.after(content);
638 var $content = $root.next();
640 // Event on.click re-assign
641 $content.find('.versions li.version a').on('click', changeVersionCompareTo);
644 $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + currentVersion + '\']').addClass('has-modifications');
647 // TODO: on change main version or select the highest version re-render
654 * Compare all currently selected Versions with their predecessor.
656 function changeAllVersionCompareTo(e) {
658 $('article:visible .versions').each(function(){
659 var $root = $(this).parents('article');
660 var currentVersion = $root.data('version');
661 var $foundElement = null;
662 $(this).find('li.version a').each(function() {
663 var selectVersion = $(this).html();
664 if (selectVersion < currentVersion && ! $foundElement)
665 $foundElement = $(this);
669 $foundElement.trigger('click');
677 function sortFields(fields_object) {
678 $.each(fields_object, function (key, fields) {
680 var reversed = fields.slice().reverse()
682 var max_dot_count = Math.max.apply(null, reversed.map(function (item) {
683 return item.field.split(".").length - 1;
686 for (var dot_count = 1; dot_count <= max_dot_count; dot_count++) {
687 reversed.forEach(function (item, index) {
688 var parts = item.field.split(".");
689 if (parts.length - 1 == dot_count) {
690 var fields_names = fields.map(function (item) { return item.field; });
691 if (parts.slice(1).length >= 1) {
692 var prefix = parts.slice(0, parts.length - 1).join(".");
693 var prefix_index = fields_names.indexOf(prefix);
694 if (prefix_index > -1) {
695 fields.splice(fields_names.indexOf(item.field), 1);
696 fields.splice(prefix_index + 1, 0, item);
706 * Add article settings.
708 function addArticleSettings(fields, entry) {
710 // TODO: replace all group-name-version in template with id.
711 fields.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
712 fields.id = fields.id.replace(/\./g, '_');
714 if (entry.header && entry.header.fields) {
715 sortFields(entry.header.fields);
716 fields._hasTypeInHeaderFields = _hasTypeInFields(entry.header.fields);
719 if (entry.parameter && entry.parameter.fields) {
720 sortFields(entry.parameter.fields);
721 fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
724 if (entry.error && entry.error.fields) {
725 sortFields(entry.error.fields);
726 fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
729 if (entry.success && entry.success.fields) {
730 sortFields(entry.success.fields);
731 fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
734 if (entry.info && entry.info.fields) {
735 sortFields(entry.info.fields);
736 fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
739 // add template settings
740 fields.template = apiProject.template;
746 function renderArticle(group, name, version) {
748 $.each(apiByGroupAndName[group][name], function(index, currentEntry) {
749 if (currentEntry.version === version)
750 entry = currentEntry;
754 versions: articleVersions[group][name]
757 addArticleSettings(fields, entry);
759 return templateArticle(fields);
763 * Render original Article and remove the current visible Article.
765 function resetArticle(group, name, version) {
766 var $root = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible');
767 var content = renderArticle(group, name, version);
769 $root.after(content);
770 var $content = $root.next();
772 // Event on.click muss neu zugewiesen werden (sollte eigentlich mit on automatisch funktionieren... sollte)
773 $content.find('.versions li.version a').on('click', changeVersionCompareTo);
775 $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('has-modifications');
784 function loadGoogleFontCss() {
788 $(window).scrollspy('refresh')
791 families: ['Source Code Pro', 'Source Sans Pro:n4,n6,n7']
797 * Return ordered entries by custom order and append not defined entries to the end.
798 * @param {String[]} elements
799 * @param {String[]} order
800 * @param {String} splitBy
801 * @return {String[]} Custom ordered list.
803 function sortByOrder(elements, order, splitBy) {
805 order.forEach (function(name) {
807 elements.forEach (function(element) {
808 var parts = element.split(splitBy);
809 var key = parts[1]; // reference keep for sorting
811 results.push(element);
814 elements.forEach (function(key) {
819 // Append all other entries that ar not defined in order
820 elements.forEach(function(element) {
821 if (results.indexOf(element) === -1)
822 results.push(element);