main.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. require.config({
  2. paths: {
  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.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. },
  16. shim: {
  17. bootstrap: {
  18. deps: ['jquery']
  19. },
  20. diffMatchPatch: {
  21. exports: 'diff_match_patch'
  22. },
  23. handlebars: {
  24. exports: 'Handlebars'
  25. },
  26. handlebarsExtended: {
  27. deps: ['jquery', 'handlebars'],
  28. exports: 'Handlebars'
  29. },
  30. prettify: {
  31. exports: 'prettyPrint'
  32. }
  33. },
  34. urlArgs: 'v=' + (new Date()).getTime(),
  35. waitSeconds: 15
  36. });
  37. require([
  38. 'jquery',
  39. 'lodash',
  40. 'locales',
  41. 'handlebarsExtended',
  42. './api_project.js',
  43. './api_data.js',
  44. 'prettify',
  45. 'utilsSampleRequest',
  46. 'semver',
  47. 'webfontloader',
  48. 'bootstrap',
  49. 'pathToRegexp'
  50. ], function($, _, locale, Handlebars, apiProject, apiData, prettyPrint, sampleRequest, semver, WebFont) {
  51. // load google web fonts
  52. loadGoogleFontCss();
  53. var api = apiData.api;
  54. //
  55. // Templates
  56. //
  57. var templateHeader = Handlebars.compile( $('#template-header').html() );
  58. var templateFooter = Handlebars.compile( $('#template-footer').html() );
  59. var templateArticle = Handlebars.compile( $('#template-article').html() );
  60. var templateCompareArticle = Handlebars.compile( $('#template-compare-article').html() );
  61. var templateGenerator = Handlebars.compile( $('#template-generator').html() );
  62. var templateProject = Handlebars.compile( $('#template-project').html() );
  63. var templateSections = Handlebars.compile( $('#template-sections').html() );
  64. var templateSidenav = Handlebars.compile( $('#template-sidenav').html() );
  65. //
  66. // apiProject defaults
  67. //
  68. if ( ! apiProject.template)
  69. apiProject.template = {};
  70. if (apiProject.template.withCompare == null)
  71. apiProject.template.withCompare = true;
  72. if (apiProject.template.withGenerator == null)
  73. apiProject.template.withGenerator = true;
  74. if (apiProject.template.forceLanguage)
  75. locale.setLanguage(apiProject.template.forceLanguage);
  76. // Setup jQuery Ajax
  77. $.ajaxSetup(apiProject.template.jQueryAjaxSetup);
  78. //
  79. // Data transform
  80. //
  81. // grouped by group
  82. var apiByGroup = _.groupBy(api, function(entry) {
  83. return entry.group;
  84. });
  85. // grouped by group and name
  86. var apiByGroupAndName = {};
  87. $.each(apiByGroup, function(index, entries) {
  88. apiByGroupAndName[index] = _.groupBy(entries, function(entry) {
  89. return entry.name;
  90. });
  91. });
  92. //
  93. // sort api within a group by title ASC and custom order
  94. //
  95. var newList = [];
  96. var umlauts = { 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // TODO: remove in version 1.0
  97. $.each (apiByGroupAndName, function(index, groupEntries) {
  98. // get titles from the first entry of group[].name[] (name has versioning)
  99. var titles = [];
  100. $.each (groupEntries, function(titleName, entries) {
  101. var title = entries[0].title;
  102. if(title !== undefined) {
  103. title.toLowerCase().replace(/[äöüß]/g, function($0) { return umlauts[$0]; });
  104. titles.push(title + '#~#' + titleName); // '#~#' keep reference to titleName after sorting
  105. }
  106. });
  107. // sort by name ASC
  108. titles.sort();
  109. // custom order
  110. if (apiProject.order)
  111. titles = sortByOrder(titles, apiProject.order, '#~#');
  112. // add single elements to the new list
  113. titles.forEach(function(name) {
  114. var values = name.split('#~#');
  115. var key = values[1];
  116. groupEntries[key].forEach(function(entry) {
  117. newList.push(entry);
  118. });
  119. });
  120. });
  121. // api overwrite with ordered list
  122. api = newList;
  123. //
  124. // Group- and Versionlists
  125. //
  126. var apiGroups = {};
  127. var apiGroupTitles = {};
  128. var apiVersions = {};
  129. apiVersions[apiProject.version] = 1;
  130. $.each(api, function(index, entry) {
  131. apiGroups[entry.group] = 1;
  132. apiGroupTitles[entry.group] = entry.groupTitle || entry.group;
  133. apiVersions[entry.version] = 1;
  134. });
  135. // sort groups
  136. apiGroups = Object.keys(apiGroups);
  137. apiGroups.sort();
  138. // custom order
  139. if (apiProject.order)
  140. apiGroups = sortByOrder(apiGroups, apiProject.order);
  141. // sort versions DESC
  142. apiVersions = Object.keys(apiVersions);
  143. apiVersions.sort(semver.compare);
  144. apiVersions.reverse();
  145. //
  146. // create Navigationlist
  147. //
  148. var nav = [];
  149. apiGroups.forEach(function(group) {
  150. // Mainmenu entry
  151. nav.push({
  152. group: group,
  153. isHeader: true,
  154. title: apiGroupTitles[group]
  155. });
  156. // Submenu
  157. var oldName = '';
  158. api.forEach(function(entry) {
  159. if (entry.group === group) {
  160. if (oldName !== entry.name) {
  161. nav.push({
  162. title: entry.title,
  163. group: group,
  164. name: entry.name,
  165. type: entry.type,
  166. version: entry.version
  167. });
  168. } else {
  169. nav.push({
  170. title: entry.title,
  171. group: group,
  172. hidden: true,
  173. name: entry.name,
  174. type: entry.type,
  175. version: entry.version
  176. });
  177. }
  178. oldName = entry.name;
  179. }
  180. });
  181. });
  182. // Mainmenu Header entry
  183. if (apiProject.header) {
  184. nav.unshift({
  185. group: '_',
  186. isHeader: true,
  187. title: (apiProject.header.title == null) ? locale.__('General') : apiProject.header.title,
  188. isFixed: true
  189. });
  190. }
  191. // Mainmenu Footer entry
  192. if (apiProject.footer && apiProject.footer.title != null) {
  193. nav.push({
  194. group: '_footer',
  195. isHeader: true,
  196. title: apiProject.footer.title,
  197. isFixed: true
  198. });
  199. }
  200. // render pagetitle
  201. var title = apiProject.title ? apiProject.title : 'apiDoc: ' + apiProject.name + ' - ' + apiProject.version;
  202. $(document).attr('title', title);
  203. // remove loader
  204. $('#loader').remove();
  205. // render sidenav
  206. var fields = {
  207. nav: nav
  208. };
  209. $('#sidenav').append( templateSidenav(fields) );
  210. // render Generator
  211. $('#generator').append( templateGenerator(apiProject) );
  212. // render Project
  213. _.extend(apiProject, { versions: apiVersions});
  214. $('#project').append( templateProject(apiProject) );
  215. // render apiDoc, header/footer documentation
  216. if (apiProject.header)
  217. $('#header').append( templateHeader(apiProject.header) );
  218. if (apiProject.footer)
  219. $('#footer').append( templateFooter(apiProject.footer) );
  220. //
  221. // Render Sections and Articles
  222. //
  223. var articleVersions = {};
  224. var content = '';
  225. apiGroups.forEach(function(groupEntry) {
  226. var articles = [];
  227. var oldName = '';
  228. var fields = {};
  229. var title = groupEntry;
  230. var description = '';
  231. articleVersions[groupEntry] = {};
  232. // render all articles of a group
  233. api.forEach(function(entry) {
  234. if(groupEntry === entry.group) {
  235. if (oldName !== entry.name) {
  236. // determine versions
  237. api.forEach(function(versionEntry) {
  238. if (groupEntry === versionEntry.group && entry.name === versionEntry.name) {
  239. if ( ! articleVersions[entry.group].hasOwnProperty(entry.name) ) {
  240. articleVersions[entry.group][entry.name] = [];
  241. }
  242. articleVersions[entry.group][entry.name].push(versionEntry.version);
  243. }
  244. });
  245. fields = {
  246. article: entry,
  247. versions: articleVersions[entry.group][entry.name]
  248. };
  249. } else {
  250. fields = {
  251. article: entry,
  252. hidden: true,
  253. versions: articleVersions[entry.group][entry.name]
  254. };
  255. }
  256. // add prefix URL for endpoint
  257. if (apiProject.url)
  258. fields.article.url = apiProject.url + fields.article.url;
  259. addArticleSettings(fields, entry);
  260. if (entry.groupTitle)
  261. title = entry.groupTitle;
  262. // TODO: make groupDescription compareable with older versions (not important for the moment)
  263. if (entry.groupDescription)
  264. description = entry.groupDescription;
  265. articles.push({
  266. article: templateArticle(fields),
  267. group: entry.group,
  268. name: entry.name
  269. });
  270. oldName = entry.name;
  271. }
  272. });
  273. // render Section with Articles
  274. var fields = {
  275. group: groupEntry,
  276. title: title,
  277. description: description,
  278. articles: articles
  279. };
  280. content += templateSections(fields);
  281. });
  282. $('#sections').append( content );
  283. // Bootstrap Scrollspy
  284. $(this).scrollspy({ target: '#scrollingNav', offset: 18 });
  285. // Content-Scroll on Navigation click.
  286. $('.sidenav').find('a').on('click', function(e) {
  287. e.preventDefault();
  288. var id = $(this).attr('href');
  289. if ($(id).length > 0)
  290. $('html,body').animate({ scrollTop: parseInt($(id).offset().top) }, 400);
  291. window.location.hash = $(this).attr('href');
  292. });
  293. // Quickjump on Pageload to hash position.
  294. if(window.location.hash) {
  295. var id = window.location.hash;
  296. if ($(id).length > 0)
  297. $('html,body').animate({ scrollTop: parseInt($(id).offset().top) }, 0);
  298. }
  299. /**
  300. * Check if Parameter (sub) List has a type Field.
  301. * Example: @apiSuccess varname1 No type.
  302. * @apiSuccess {String} varname2 With type.
  303. *
  304. * @param {Object} fields
  305. */
  306. function _hasTypeInFields(fields) {
  307. var result = false;
  308. $.each(fields, function(name) {
  309. if (_.any(fields[name], function(item) { return item.type; }) )
  310. result = true;
  311. });
  312. return result;
  313. }
  314. /**
  315. * On Template changes, recall plugins.
  316. */
  317. function initDynamic() {
  318. // bootstrap popover
  319. $('a[data-toggle=popover]')
  320. .popover()
  321. .click(function(e) {
  322. e.preventDefault();
  323. })
  324. ;
  325. var version = $('#version strong').html();
  326. $('#sidenav li').removeClass('is-new');
  327. if (apiProject.template.withCompare) {
  328. $('#sidenav li[data-version=\'' + version + '\']').each(function(){
  329. var group = $(this).data('group');
  330. var name = $(this).data('name');
  331. var length = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').length;
  332. var index = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').index($(this));
  333. if (length === 1 || index === (length - 1))
  334. $(this).addClass('is-new');
  335. });
  336. }
  337. // tabs
  338. $('.nav-tabs-examples a').click(function (e) {
  339. e.preventDefault();
  340. $(this).tab('show');
  341. });
  342. $('.nav-tabs-examples').find('a:first').tab('show');
  343. // sample request switch
  344. $('.sample-request-switch').click(function (e) {
  345. var name = '.' + $(this).attr('name') + '-fields';
  346. $(name).addClass('hide');
  347. $(this).parent().next(name).removeClass('hide');
  348. });
  349. // call scrollspy refresh method
  350. $(window).scrollspy('refresh');
  351. // init modules
  352. sampleRequest.initDynamic();
  353. }
  354. initDynamic();
  355. // Pre- / Code-Format
  356. prettyPrint();
  357. //
  358. // HTML-Template specific jQuery-Functions
  359. //
  360. // Change Main Version
  361. $('#versions li.version a').on('click', function(e) {
  362. e.preventDefault();
  363. var selectedVersion = $(this).html();
  364. $('#version strong').html(selectedVersion);
  365. // hide all
  366. $('article').addClass('hide');
  367. $('#sidenav li:not(.nav-fixed)').addClass('hide');
  368. // show 1st equal or lower Version of each entry
  369. $('article[data-version]').each(function(index) {
  370. var group = $(this).data('group');
  371. var name = $(this).data('name');
  372. var version = $(this).data('version');
  373. if (version <= selectedVersion) {
  374. if ($('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible').length === 0) {
  375. // enable Article
  376. $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
  377. // enable Navigation
  378. $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
  379. $('#sidenav li.nav-header[data-group=\'' + group + '\']').removeClass('hide');
  380. }
  381. }
  382. });
  383. // show 1st equal or lower Version of each entry
  384. $('article[data-version]').each(function(index) {
  385. var group = $(this).data('group');
  386. $('section#api-' + group).removeClass('hide');
  387. if ($('section#api-' + group + ' article:visible').length === 0) {
  388. $('section#api-' + group).addClass('hide');
  389. } else {
  390. $('section#api-' + group).removeClass('hide');
  391. }
  392. });
  393. initDynamic();
  394. return;
  395. });
  396. // compare all article with their predecessor
  397. $('#compareAllWithPredecessor').on('click', changeAllVersionCompareTo);
  398. // change version of an article
  399. $('article .versions li.version a').on('click', changeVersionCompareTo);
  400. // compare url-parameter
  401. $.urlParam = function(name) {
  402. var results = new RegExp('[\\?&amp;]' + name + '=([^&amp;#]*)').exec(window.location.href);
  403. return (results && results[1]) ? results[1] : null;
  404. };
  405. if ($.urlParam('compare')) {
  406. // URL Paramter ?compare=1 is set
  407. $('#compareAllWithPredecessor').trigger('click');
  408. if (window.location.hash) {
  409. var id = window.location.hash;
  410. $('html,body').animate({ scrollTop: parseInt($(id).offset().top) - 18 }, 0);
  411. }
  412. }
  413. /**
  414. * Change version of an article to compare it to an other version.
  415. */
  416. function changeVersionCompareTo(e) {
  417. e.preventDefault();
  418. var $root = $(this).parents('article');
  419. var selectedVersion = $(this).html();
  420. var $button = $root.find('.version');
  421. var currentVersion = $button.find('strong').html();
  422. $button.find('strong').html(selectedVersion);
  423. var group = $root.data('group');
  424. var name = $root.data('name');
  425. var version = $root.data('version');
  426. var compareVersion = $root.data('compare-version');
  427. if (compareVersion === selectedVersion)
  428. return;
  429. if ( ! compareVersion && version == selectedVersion)
  430. return;
  431. if (compareVersion && articleVersions[group][name][0] === selectedVersion || version === selectedVersion) {
  432. // the version of the entry is set to the highest version (reset)
  433. resetArticle(group, name, version);
  434. } else {
  435. var $compareToArticle = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + selectedVersion + '\']');
  436. var sourceEntry = {};
  437. var compareEntry = {};
  438. $.each(apiByGroupAndName[group][name], function(index, entry) {
  439. if (entry.version === version)
  440. sourceEntry = entry;
  441. if (entry.version === selectedVersion)
  442. compareEntry = entry;
  443. });
  444. var fields = {
  445. article: sourceEntry,
  446. compare: compareEntry,
  447. versions: articleVersions[group][name]
  448. };
  449. // add unique id
  450. // TODO: replace all group-name-version in template with id.
  451. fields.article.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
  452. fields.article.id = fields.article.id.replace(/\./g, '_');
  453. fields.compare.id = fields.compare.group + '-' + fields.compare.name + '-' + fields.compare.version;
  454. fields.compare.id = fields.compare.id.replace(/\./g, '_');
  455. var entry = sourceEntry;
  456. if (entry.parameter && entry.parameter.fields)
  457. fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
  458. if (entry.error && entry.error.fields)
  459. fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
  460. if (entry.success && entry.success.fields)
  461. fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
  462. if (entry.info && entry.info.fields)
  463. fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
  464. var entry = compareEntry;
  465. if (fields._hasTypeInParameterFields !== true && entry.parameter && entry.parameter.fields)
  466. fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
  467. if (fields._hasTypeInErrorFields !== true && entry.error && entry.error.fields)
  468. fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
  469. if (fields._hasTypeInSuccessFields !== true && entry.success && entry.success.fields)
  470. fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
  471. if (fields._hasTypeInInfoFields !== true && entry.info && entry.info.fields)
  472. fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
  473. var content = templateCompareArticle(fields);
  474. $root.after(content);
  475. var $content = $root.next();
  476. // Event on.click re-assign
  477. $content.find('.versions li.version a').on('click', changeVersionCompareTo);
  478. // select navigation
  479. $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + currentVersion + '\']').addClass('has-modifications');
  480. $root.remove();
  481. // TODO: on change main version or select the highest version re-render
  482. }
  483. initDynamic();
  484. }
  485. /**
  486. * Compare all currently selected Versions with their predecessor.
  487. */
  488. function changeAllVersionCompareTo(e) {
  489. e.preventDefault();
  490. $('article:visible .versions').each(function(){
  491. var $root = $(this).parents('article');
  492. var currentVersion = $root.data('version');
  493. var $foundElement = null;
  494. $(this).find('li.version a').each(function() {
  495. var selectVersion = $(this).html();
  496. if (selectVersion < currentVersion && ! $foundElement)
  497. $foundElement = $(this);
  498. });
  499. if($foundElement)
  500. $foundElement.trigger('click');
  501. });
  502. initDynamic();
  503. }
  504. /**
  505. * Sort the fields.
  506. */
  507. function sortFields(fields_object) {
  508. $.each(fields_object, function (key, fields) {
  509. var reversed = fields.slice().reverse()
  510. var max_dot_count = Math.max.apply(null, reversed.map(function (item) {
  511. return item.field.split(".").length - 1;
  512. }))
  513. for (var dot_count = 1; dot_count <= max_dot_count; dot_count++) {
  514. reversed.forEach(function (item, index) {
  515. var parts = item.field.split(".");
  516. if (parts.length - 1 == dot_count) {
  517. var fields_names = fields.map(function (item) { return item.field; });
  518. if (parts.slice(1).length >= 1) {
  519. var prefix = parts.slice(0, parts.length - 1).join(".");
  520. var prefix_index = fields_names.indexOf(prefix);
  521. if (prefix_index > -1) {
  522. fields.splice(fields_names.indexOf(item.field), 1);
  523. fields.splice(prefix_index + 1, 0, item);
  524. }
  525. }
  526. }
  527. });
  528. }
  529. });
  530. }
  531. /**
  532. * Add article settings.
  533. */
  534. function addArticleSettings(fields, entry) {
  535. // add unique id
  536. // TODO: replace all group-name-version in template with id.
  537. fields.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
  538. fields.id = fields.id.replace(/\./g, '_');
  539. if (entry.header && entry.header.fields) {
  540. sortFields(entry.header.fields);
  541. fields._hasTypeInHeaderFields = _hasTypeInFields(entry.header.fields);
  542. }
  543. if (entry.parameter && entry.parameter.fields) {
  544. sortFields(entry.parameter.fields);
  545. fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
  546. }
  547. if (entry.error && entry.error.fields) {
  548. sortFields(entry.error.fields);
  549. fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
  550. }
  551. if (entry.success && entry.success.fields) {
  552. sortFields(entry.success.fields);
  553. fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
  554. }
  555. if (entry.info && entry.info.fields) {
  556. sortFields(entry.info.fields);
  557. fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
  558. }
  559. // add template settings
  560. fields.template = apiProject.template;
  561. }
  562. /**
  563. * Render Article.
  564. */
  565. function renderArticle(group, name, version) {
  566. var entry = {};
  567. $.each(apiByGroupAndName[group][name], function(index, currentEntry) {
  568. if (currentEntry.version === version)
  569. entry = currentEntry;
  570. });
  571. var fields = {
  572. article: entry,
  573. versions: articleVersions[group][name]
  574. };
  575. addArticleSettings(fields, entry);
  576. return templateArticle(fields);
  577. }
  578. /**
  579. * Render original Article and remove the current visible Article.
  580. */
  581. function resetArticle(group, name, version) {
  582. var $root = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible');
  583. var content = renderArticle(group, name, version);
  584. $root.after(content);
  585. var $content = $root.next();
  586. // Event on.click muss neu zugewiesen werden (sollte eigentlich mit on automatisch funktionieren... sollte)
  587. $content.find('.versions li.version a').on('click', changeVersionCompareTo);
  588. $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('has-modifications');
  589. $root.remove();
  590. return;
  591. }
  592. /**
  593. * Load google fonts.
  594. */
  595. function loadGoogleFontCss() {
  596. WebFont.load({
  597. active: function() {
  598. // Update scrollspy
  599. $(window).scrollspy('refresh')
  600. },
  601. google: {
  602. families: ['Source Code Pro', 'Source Sans Pro:n4,n6,n7']
  603. }
  604. });
  605. }
  606. /**
  607. * Return ordered entries by custom order and append not defined entries to the end.
  608. * @param {String[]} elements
  609. * @param {String[]} order
  610. * @param {String} splitBy
  611. * @return {String[]} Custom ordered list.
  612. */
  613. function sortByOrder(elements, order, splitBy) {
  614. var results = [];
  615. order.forEach (function(name) {
  616. if (splitBy)
  617. elements.forEach (function(element) {
  618. var parts = element.split(splitBy);
  619. var key = parts[1]; // reference keep for sorting
  620. if (key == name)
  621. results.push(element);
  622. });
  623. else
  624. elements.forEach (function(key) {
  625. if (key == name)
  626. results.push(name);
  627. });
  628. });
  629. // Append all other entries that ar not defined in order
  630. elements.forEach(function(element) {
  631. if (results.indexOf(element) === -1)
  632. results.push(element);
  633. });
  634. return results;
  635. }
  636. });