diff --git a/PKG-INFO b/PKG-INFO index 6f3a3ad8..8776e302 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,10 +1,10 @@ Metadata-Version: 1.0 Name: swh.web -Version: 0.0.124 +Version: 0.0.125 Summary: Software Heritage Web UI Home-page: https://forge.softwareheritage.org/diffusion/DWUI/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN diff --git a/README.md b/README.md index 02f1e2b5..5ab6749e 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,109 @@ # swh-web This repository holds the developement of Software Heritage web applications: * SWH Web API (https://archive.softwareheritage.org/api): enables to query the content of the SWH archive through HTTP requests and get responses in JSON or YAML. * SWH Web browse (https://archive.softwareheritage.org/browse): graphical interface that eases the navigation in the SWH archive. Documentation about how to use these components but also the details of their URI schemes can be found in the docs folder. The produced HTML documentation can be read and browsed at https://docs.softwareheritage.org/devel/swh-web/index.html. ## Technical details Those applications are powered by: * [Django Web Framework](https://www.djangoproject.com/) on the backend side with the following extensions enabled: * [django-rest-framework](http://www.django-rest-framework.org/) * [django-webpack-loader](https://github.com/owais/django-webpack-loader) * [django-js-reverse](http://django-js-reverse.readthedocs.io/en/latest/) * [webpack](https://webpack.js.org/) on the frontend side for better static assets management, including: * assets dependencies management and retrieval through [npm](https://www.npmjs.com/) * linting of custom javascript code (through [eslint](https://eslint.org/)) and stylesheets (through [stylelint](https://stylelint.io/)) * use of [es6](http://es6-features.org) syntax and advanced javascript feature like [async/await](https://javascript.info/async-await) or [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) thanks to [babel](https://babeljs.io/) (es6 to es5 transpiler and polyfills provider) * assets minication (using [UglifyJS](https://github.com/mishoo/UglifyJS2) and [cssnano](http://cssnano.co/)) but also dead code elimination for production use ## How to build and run ### Requirements First you will need [Python 3](https://www.python.org) and a complete [swh development environment](https://forge.softwareheritage.org/source/swh-environment/) installed. To run the backend, you need to have the following Python 3 modules installed: * beautifulsoup4 * django >= 1.10.7 * djangorestframework >= 3.4.0 * django_webpack_loader * django_js_reverse * docutils * file_magic >= 0.3.0 * htmlmin * lxml * pygments * python-dateutil * pyyaml To compile the frontend assets, you need to have [nodejs](https://nodejs.org/en/) > 4.x (preferably version 8.x) and [npm](https://www.npmjs.com/) (or [yarn](https://yarnpkg.com/en/)) installed. If you are on Debian, you can easily install an up to date nodejs/npm by following the instructions located at https://github.com/nodesource/distributions. Once you have installed nodejs, issue the following command in the root directory of swh-web in order to retrieve all the frontend dependencies: ``` $ npm install ``` or if you prefer to use yarn (faster than npm): ``` $ yarn install ``` Please note that the static assets bundles generated by webpack are not stored in the git repository. Follow the instructions below in order to generate them in order to be able to run the frontend part of the web applications. ### Make targets Below is the list of available make targets that can be executed from the root directory of swh-web in order to build and/or execute the web applications under various configurations: * **run-django-webpack-dev-server**: Compile and serve not optimized (without mignification and dead code elimination) frontend static assets using [webpack-dev-server](https://github.com/webpack/webpack-dev-server) and run django server with development settings. This is the recommended target to use when developing swh-web as it enables automatic reloading of backend and frontend part of the applications when modifying source files (*.py, *.js, *.css, *.html). * **run-django-webpack-dev**: Compile not optimized (no mignification, no dead code elimination) frontend static assets using webpack and run django server with development settings. This is the recommended target when one only wants to develop the backend side of the application. -* **run-django-webpack-prod**: Compile optimized (with mignification and dead code elimination) frontend static assets using webpack and run django server with production settings. This is usefull to test the applications in production mode (with the difference that static assets are served by django). Production settings notably enables advanced django caching -and you will need to have [memcached](https://memcached.org/) installed for that feature to work. +* **run-django-webpack-prod**: Compile optimized (with mignification and dead code elimination) frontend static assets using webpack and run django server with production settings. This is usefull to test the applications in production mode (with the difference that static assets are served by django). Production settings notably enables advanced django caching and you will need to have [memcached](https://memcached.org/) installed for that feature to work. * **run-django-server-dev**: Run the django server with development settings but without compiling frontend static assets through webpack. * **run-django-server-prod**: Run the django server with production settings but without compiling frontend static assets through webpack. * **run-gunicorn-server**: Run the web applications with production settings in a [gunicorn](http://gunicorn.org/) worker as they will be in real production environment. Once one of these targets executed, the web applications can be executed by pointing your browser to http://localhost:5004. ### Npm/Yarn targets Below is a list of available npm/yarn targets in order to only execute the frontend static assets compilation (no web server will be executed): * **build-dev**: compile not optimized (without mignification and dead code elimination) frontend static assets and store the results in the `swh/web/static` folder. * **build**: compile optimized (with mignification and dead code elimination) frontend static assets and store the results in the `swh/web/static` folder. **The build target must be executed prior performing the Debian packaging of swh-web** in order for the package to contain the optimized assets dedicated to production environment. To execute these targets, issue the following commmand: ``` $ npm run ``` or if you prefer using yarn: ``` $ yarn ``` diff --git a/swh.web.egg-info/PKG-INFO b/swh.web.egg-info/PKG-INFO index 6f3a3ad8..8776e302 100644 --- a/swh.web.egg-info/PKG-INFO +++ b/swh.web.egg-info/PKG-INFO @@ -1,10 +1,10 @@ Metadata-Version: 1.0 Name: swh.web -Version: 0.0.124 +Version: 0.0.125 Summary: Software Heritage Web UI Home-page: https://forge.softwareheritage.org/diffusion/DWUI/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN diff --git a/swh/web/assets/src/bundles/browse/origin-search.js b/swh/web/assets/src/bundles/browse/origin-search.js index 1313e2aa..a32580ee 100644 --- a/swh/web/assets/src/bundles/browse/origin-search.js +++ b/swh/web/assets/src/bundles/browse/origin-search.js @@ -1,152 +1,152 @@ import {heapsPermute} from 'utils/heaps-permute'; import {handleFetchError} from 'utils/functions'; let originPatterns; let perPage = 15; let limit = perPage * 10; let offset = 0; let currentData = null; let inSearch = false; function fixTableRowsStyle() { setTimeout(() => { $('#origin-search-results tbody tr').removeAttr('style'); }); } function populateOriginSearchResultsTable(data, offset) { let localOffset = offset % limit; if (data.length > 0) { $('#swh-origin-search-results').show(); $('#swh-no-origins-found').hide(); $('#origin-search-results tbody tr').remove(); let table = $('#origin-search-results tbody'); for (let i = localOffset; i < localOffset + perPage && i < data.length; ++i) { let elem = data[i]; let tableRow = ''; tableRow += '' + elem.type + ''; let browseUrl = Urls.browse_origin(elem.type, elem.url); tableRow += '' + browseUrl + ''; tableRow += ''; tableRow += ''; table.append(tableRow); // get async latest visit snapshot and update visit status icon let latestSnapshotUrl = Urls.browse_origin_latest_snapshot(elem.id); - fetch(latestSnapshotUrl) + fetch(latestSnapshotUrl, {credentials: 'same-origin'}) .then(response => response.json()) .then(data => { let originId = elem.id; $('#visit-status-origin-' + originId).children().remove(); if (data) { $('#visit-status-origin-' + originId).append(''); } else { $('#visit-status-origin-' + originId).append(''); } }); } fixTableRowsStyle(); } else { $('#swh-origin-search-results').hide(); $('#swh-no-origins-found').show(); } if (data.length - localOffset < perPage || (data.length < limit && (localOffset + perPage) === data.length)) { $('#origins-next-results-button').addClass('disabled'); } else { $('#origins-next-results-button').removeClass('disabled'); } if (offset > 0) { $('#origins-prev-results-button').removeClass('disabled'); } else { $('#origins-prev-results-button').addClass('disabled'); } inSearch = false; setTimeout(() => { window.scrollTo(0, 0); }); } function searchOrigins(patterns, limit, searchOffset, offset) { originPatterns = patterns; let patternsArray = patterns.trim().replace(/\s+/g, ' ').split(' '); let patternsPermut = []; heapsPermute(patternsArray, p => patternsPermut.push(p.join('.*'))); let regex = patternsPermut.join('|'); let searchUrl = Urls.browse_origin_search(regex) + `?limit=${limit}&offset=${searchOffset}®exp=true`; $('.swh-loading').addClass('show'); - fetch(searchUrl) + fetch(searchUrl, {credentials: 'same-origin'}) .then(handleFetchError) .then(response => response.json()) .then(data => { currentData = data; if (typeof Storage !== 'undefined') { sessionStorage.setItem('last-swh-origin-url-patterns', patterns); sessionStorage.setItem('last-swh-origin-search-results', JSON.stringify(data)); sessionStorage.setItem('last-swh-origin-search-offset', offset); } $('.swh-loading').removeClass('show'); populateOriginSearchResultsTable(data, offset); }) .catch(() => { $('.swh-loading').removeClass('show'); inSearch = false; }); } export function initOriginSearch() { $(document).ready(() => { if (typeof Storage !== 'undefined') { originPatterns = sessionStorage.getItem('last-swh-origin-url-patterns'); let data = sessionStorage.getItem('last-swh-origin-search-results'); offset = sessionStorage.getItem('last-swh-origin-search-offset'); if (data) { $('#origins-url-patterns').val(originPatterns); offset = parseInt(offset); populateOriginSearchResultsTable(JSON.parse(data), offset); } } $('#search_origins').submit(event => { let patterns = $('#origins-url-patterns').val(); offset = 0; inSearch = true; searchOrigins(patterns, limit, offset, offset); event.preventDefault(); }); $('#origins-next-results-button').click(event => { if ($('#origins-next-results-button').hasClass('disabled') || inSearch) { return; } inSearch = true; offset += perPage; if (!currentData || offset % limit === 0) { searchOrigins(originPatterns, limit, offset, offset); } else { populateOriginSearchResultsTable(currentData, offset); } event.preventDefault(); }); $('#origins-prev-results-button').click(event => { if ($('#origins-prev-results-button').hasClass('disabled') || inSearch) { return; } inSearch = true; offset -= perPage; if (!currentData || (offset > 0 && (offset + perPage) % limit === 0)) { searchOrigins(originPatterns, limit, (offset + perPage) - limit, offset); } else { populateOriginSearchResultsTable(currentData, offset); } event.preventDefault(); }); $(document).on('shown.bs.tab', 'a[data-toggle="tab"]', e => { if (e.currentTarget.text.trim() === 'Search') { fixTableRowsStyle(); } }); }); } diff --git a/swh/web/assets/src/bundles/revision/diff-utils.js b/swh/web/assets/src/bundles/revision/diff-utils.js index 6f594885..edbaf843 100644 --- a/swh/web/assets/src/bundles/revision/diff-utils.js +++ b/swh/web/assets/src/bundles/revision/diff-utils.js @@ -1,515 +1,515 @@ import 'waypoints/lib/jquery.waypoints'; import {staticAsset} from 'utils/functions'; // path to static spinner asset let swhSpinnerSrc = staticAsset('img/swh-spinner.gif'); // number of changed files in the revision let changes = null; let nbChangedFiles = 0; // to track the number of already computed files diffs let nbDiffsComputed = 0; // the no newline at end of file marker from Github let noNewLineMarker = ` `; // to track the total number of added lines in files diffs let nbAdditions = 0; // to track the total number of deleted lines in files diffs let nbDeletions = 0; // to track the already computed diffs by id let computedDiffs = {}; // map a diff id to its computation url let diffsUrls = {}; // to check if a DOM element is in the viewport function isInViewport(elt) { let elementTop = $(elt).offset().top; let elementBottom = elementTop + $(elt).outerHeight(); let viewportTop = $(window).scrollTop(); let viewportBottom = viewportTop + $(window).height(); return elementBottom > viewportTop && elementTop < viewportBottom; } // to format the diffs line numbers function formatDiffLineNumbers(fromLine, toLine, maxNumberChars) { let ret = ''; if (fromLine != null) { for (let i = 0; i < (maxNumberChars - fromLine.length); ++i) { ret += ' '; } ret += fromLine; } if (fromLine != null && toLine != null) { ret += ' '; } if (toLine != null) { for (let i = 0; i < (maxNumberChars - toLine.length); ++i) { ret += ' '; } ret += toLine; } return ret; } // to compute diff and process it for display export function computeDiff(diffUrl, diffId) { // force diff computation ? let force = diffUrl.indexOf('force=true') !== -1; // it no forced computation and diff already computed, do nothing if (!force && computedDiffs.hasOwnProperty(diffId)) { return; } // mark diff computation as already requested computedDiffs[diffId] = true; $(`#${diffId}-loading`).css('visibility', 'visible'); // set spinner visible while requesting diff $(`#${diffId}-loading`).css('display', 'block'); $(`#${diffId}-highlightjs`).css('display', 'none'); // request diff computation and process it - fetch(diffUrl) + fetch(diffUrl, {credentials: 'same-origin'}) .then(response => response.json()) .then(data => { // increment number of computed diffs ++nbDiffsComputed; // toggle the 'Compute all diffs' button if all diffs have been computed if (nbDiffsComputed === changes.length) { $('#swh-compute-all-diffs').addClass('active'); } // Large diff (> threshold) are not automatically computed, // add a button to force its computation if (data.diff_str.indexOf('Large diff') === 0) { $(`#${diffId}`)[0].innerHTML = data.diff_str + `
`; setDiffVisible(diffId); } else if (data.diff_str.indexOf('@@') !== 0) { $(`#${diffId}`).text(data.diff_str); setDiffVisible(diffId); } else { // prepare code highlighting $(`.${diffId}`).removeClass('nohighlight-swh'); $(`.${diffId}`).addClass(data.language); // set unified diff text $(`#${diffId}`).text(data.diff_str); // code highlighting for unified diff $(`#${diffId}`).each((i, block) => { hljs.highlightBlock(block); hljs.lineNumbersBlock(block); }); // hljs.lineNumbersBlock is asynchronous so we have to postpone our // next treatments by adding it at the end of the current js events queue setTimeout(() => { // process unified diff lines in order to generate side-by-side diffs text // but also compute line numbers for unified and side-by-side difss let linesInfoRegExp = new RegExp(/^@@ -(\d+),(\d+) \+(\d+),(\d+) @@$/gm); let baseFromLine = ''; let baseToLine = ''; let fromToLines = []; let fromLines = []; let toLines = []; let maxNumberChars = 0; let diffFromStr = ''; let diffToStr = ''; let linesOffset = 0; $(`#${diffId} .hljs-ln-numbers`).each((i, lnElt) => { let lnText = lnElt.nextSibling.innerText; let linesInfo = linesInfoRegExp.exec(lnText); let fromLine = ''; let toLine = ''; // parsed lines info from the diff output if (linesInfo) { baseFromLine = parseInt(linesInfo[1]) - 1; baseToLine = parseInt(linesInfo[3]) - 1; linesOffset = 0; diffFromStr += (lnText + '\n'); diffToStr += (lnText + '\n'); fromLines.push(''); toLines.push(''); // line removed in the from file } else if (lnText.length > 0 && lnText[0] === '-') { baseFromLine = baseFromLine + 1; fromLine = baseFromLine.toString(); fromLines.push(fromLine); ++nbDeletions; diffFromStr += (lnText + '\n'); ++linesOffset; // line added in the from file } else if (lnText.length > 0 && lnText[0] === '+') { baseToLine = baseToLine + 1; toLine = baseToLine.toString(); toLines.push(toLine); ++nbAdditions; diffToStr += (lnText + '\n'); --linesOffset; // line present in both files } else { baseFromLine = baseFromLine + 1; baseToLine = baseToLine + 1; fromLine = baseFromLine.toString(); toLine = baseToLine.toString(); for (let j = 0; j < Math.abs(linesOffset); ++j) { if (linesOffset > 0) { diffToStr += '\n'; toLines.push(''); } else { diffFromStr += '\n'; fromLines.push(''); } } linesOffset = 0; diffFromStr += (lnText + '\n'); diffToStr += (lnText + '\n'); toLines.push(toLine); fromLines.push(fromLine); } if (!baseFromLine) { fromLine = ''; } if (!baseToLine) { toLine = ''; } fromToLines[i] = [fromLine, toLine]; maxNumberChars = Math.max(maxNumberChars, fromLine.length); maxNumberChars = Math.max(maxNumberChars, toLine.length); }); // set side-by-side diffs text $(`#${diffId}-from`).text(diffFromStr); $(`#${diffId}-to`).text(diffToStr); // code highlighting for side-by-side diffs $(`#${diffId}-from, #${diffId}-to`).each((i, block) => { hljs.highlightBlock(block); hljs.lineNumbersBlock(block); }); // hljs.lineNumbersBlock is asynchronous so we have to postpone our // next treatments by adding it at the end of the current js events queue setTimeout(() => { // diff highlighting for added/removed lines on top of code highlighting $(`.${diffId} .hljs-ln-numbers`).each((i, lnElt) => { let lnText = lnElt.nextSibling.innerText; let linesInfo = linesInfoRegExp.exec(lnText); if (linesInfo) { $(lnElt).parent().addClass('swh-diff-lines-info'); let linesInfoText = $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').text(); $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').children().remove(); $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').text(''); $(lnElt).parent().find('.hljs-ln-code .hljs-ln-line').append(`${linesInfoText}`); } else if (lnText.length > 0 && lnText[0] === '-') { $(lnElt).parent().addClass('swh-diff-removed-line'); } else if (lnText.length > 0 && lnText[0] === '+') { $(lnElt).parent().addClass('swh-diff-added-line'); } }); // set line numbers for unified diff $(`#${diffId} .hljs-ln-numbers`).each((i, lnElt) => { $(lnElt).children().attr('data-line-number', formatDiffLineNumbers(fromToLines[i][0], fromToLines[i][1], maxNumberChars)); }); // set line numbers for the from side-by-side diff $(`#${diffId}-from .hljs-ln-numbers`).each((i, lnElt) => { $(lnElt).children().attr('data-line-number', formatDiffLineNumbers(fromLines[i], null, maxNumberChars)); }); // set line numbers for the to side-by-side diff $(`#${diffId}-to .hljs-ln-numbers`).each((i, lnElt) => { $(lnElt).children().attr('data-line-number', formatDiffLineNumbers(null, toLines[i], maxNumberChars)); }); // last processings: // - remove the '+' and '-' at the beginning of the diff lines // from code highlighting // - add the "no new line at end of file marker" if needed $(`.${diffId} .hljs-ln-line`).each((i, lnElt) => { if (lnElt.firstChild) { if (lnElt.firstChild.nodeName !== '#text') { let lineText = lnElt.firstChild.innerHTML; if (lineText[0] === '-' || lineText[0] === '+') { lnElt.firstChild.innerHTML = lineText.substr(1); let newTextNode = document.createTextNode(lineText[0]); $(lnElt).prepend(newTextNode); } } $(lnElt).contents().filter((i, elt) => { return elt.nodeType === 3; // Node.TEXT_NODE }).each((i, textNode) => { let swhNoNewLineMarker = '[swh-no-nl-marker]'; if (textNode.textContent.indexOf(swhNoNewLineMarker) !== -1) { textNode.textContent = textNode.textContent.replace(swhNoNewLineMarker, ''); $(lnElt).append($(noNewLineMarker)); } }); } }); // remove empty last line added by highlight.js $(`#${diffId} tr:last`).remove(); $(`#${diffId}-from tr:last`).remove(); $(`#${diffId}-to tr:last`).remove(); $(`#${diffId}-from tr:last`).remove(); $(`#${diffId}-to tr:last`).remove(); // hide the diff mode switch button in case of not generated diffs if (data.diff_str.indexOf('Diffs are not generated for non textual content') !== 0) { $(`#panel_${diffId} .diff-styles`).css('visibility', 'visible'); } setDiffVisible(diffId); }); }); } }); } function setDiffVisible(diffId) { // set the unified diff visible by default $(`#${diffId}-loading`).css('display', 'none'); $(`#${diffId}-highlightjs`).css('display', 'block'); // update displayed counters $('#swh-revision-lines-added').text(`${nbAdditions}' additions`); $('#swh-revision-lines-deleted').text(`${nbDeletions} deletions`); $('#swh-nb-diffs-computed').text(nbDiffsComputed); // refresh the waypoints triggering diffs computation as // the DOM layout has been updated Waypoint.refreshAll(); } // to compute all visible diffs in the viewport function computeVisibleDiffs() { $('.swh-file-diff-panel').each((i, elt) => { if (isInViewport(elt)) { let diffId = elt.id.replace('panel_', ''); computeDiff(diffsUrls[diffId], diffId); } }); } function genDiffPanel(diffData) { let diffPanelTitle = diffData.path; if (diffData.type === 'rename') { diffPanelTitle = `${diffData.from_path} → ${diffData.to_path}`; } let diffPanelHtml = `
${diffPanelTitle}
View file
`; return diffPanelHtml; } // setup waypoints to request diffs computation on the fly while scrolling function setupWaypoints() { for (let i = 0; i < changes.length; ++i) { let diffData = changes[i]; // create a waypoint that will trigger diff computation when // the top of the diff panel hits the bottom of the viewport $(`#panel_${diffData.id}`).waypoint({ handler: function() { if (isInViewport(this.element)) { let diffId = this.element.id.replace('panel_', ''); computeDiff(diffsUrls[diffId], diffId); this.destroy(); } }, offset: '100%' }); // create a waypoint that will trigger diff computation when // the bottom of the diff panel hits the top of the viewport $(`#panel_${diffData.id}`).waypoint({ handler: function() { if (isInViewport(this.element)) { let diffId = this.element.id.replace('panel_', ''); computeDiff(diffsUrls[diffId], diffId); this.destroy(); } }, offset: function() { return -$(this.element).height(); } }); } Waypoint.refreshAll(); } // callback to switch from side-by-side diff to unified one export function showUnifiedDiff(event, diffId) { $(`#${diffId}-splitted-diff`).css('display', 'none'); $(`#${diffId}-unified-diff`).css('display', 'block'); $(event.currentTarget).parent().find('.splitted-diff-button').removeClass('active'); $(event.currentTarget).parent().find('.unified-diff-button').addClass('active'); } // callback to switch from unified diff to side-by-side one export function showSplittedDiff(event, diffId) { $(`#${diffId}-unified-diff`).css('display', 'none'); $(`#${diffId}-splitted-diff`).css('display', 'block'); $(event.currentTarget).parent().find('.unified-diff-button').removeClass('active'); $(event.currentTarget).parent().find('.splitted-diff-button').addClass('active'); } // callback when the user clicks on the 'Compute all diffs' button export function computeAllDiffs(event) { $(event.currentTarget).addClass('active'); for (let diffId in diffsUrls) { if (diffsUrls.hasOwnProperty(diffId)) { computeDiff(diffsUrls[diffId], diffId); } } event.stopPropagation(); } export async function initRevisionDiff(revisionMessageBody, diffRevisionUrl) { await import(/* webpackChunkName: "highlightjs" */ 'utils/highlightjs'); // callback when the 'Changes' tab is activated $(document).on('shown.bs.tab', 'a[data-toggle="tab"]', e => { if (e.currentTarget.text.trim() === 'Changes') { $('#readme-panel').css('display', 'none'); if (changes) { return; } // request computation of revision file changes list // when navigating to the 'Changes' tab and add diff panels // to the DOM when receiving the result - fetch(diffRevisionUrl) + fetch(diffRevisionUrl, {credentials: 'same-origin'}) .then(response => response.json()) .then(data => { changes = data.changes; nbChangedFiles = data.total_nb_changes; let changedFilesText = `${nbChangedFiles} changed file`; if (nbChangedFiles !== 1) { changedFilesText += 's'; } $('#swh-revision-changed-files').text(changedFilesText); $('#swh-total-nb-diffs').text(changes.length); $('#swh-revision-changes-list pre')[0].innerHTML = data.changes_msg; $('#swh-revision-changes-loading').css('display', 'none'); $('#swh-revision-changes-list pre').css('display', 'block'); $('#swh-compute-all-diffs').css('visibility', 'visible'); $('#swh-revision-changes-list').removeClass('in'); if (nbChangedFiles > changes.length) { $('#swh-too-large-revision-diff').css('display', 'block'); $('#swh-nb-loaded-diffs').text(changes.length); } for (let i = 0; i < changes.length; ++i) { let diffData = changes[i]; diffsUrls[diffData.id] = diffData.diff_url; $('#swh-revision-diffs').append(genDiffPanel(diffData)); } setupWaypoints(); computeVisibleDiffs(); }); } else if (e.currentTarget.text.trim() === 'Files') { $('#readme-panel').css('display', 'block'); } }); $(document).ready(() => { if (revisionMessageBody.length > 0) { $('#swh-revision-message').addClass('in'); } else { $('#swh-collapse-revision-message').attr('data-toggle', ''); } let $root = $('html, body'); // callback when the user requests to scroll on a specific diff or back to top $('#swh-revision-changes-list a[href^="#"], #back-to-top a[href^="#"]').click(e => { let href = $.attr(e.currentTarget, 'href'); // disable waypoints while scrolling as we do not want to // launch computation of diffs the user is not interested in // (file changes list can be large) Waypoint.disableAll(); $root.animate( { scrollTop: $(href).offset().top }, { duration: 500, complete: () => { window.location.hash = href; // enable waypoints back after scrolling Waypoint.enableAll(); // compute diffs visible in the viewport computeVisibleDiffs(); } }); return false; }); }); } diff --git a/swh/web/assets/src/bundles/vault/vault-create-tasks.js b/swh/web/assets/src/bundles/vault/vault-create-tasks.js index fc55b0fc..aef446dc 100644 --- a/swh/web/assets/src/bundles/vault/vault-create-tasks.js +++ b/swh/web/assets/src/bundles/vault/vault-create-tasks.js @@ -1,83 +1,83 @@ import {handleFetchError} from 'utils/functions'; function addVaultCookingTask(cookingTask) { let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks')); if (!vaultCookingTasks) { vaultCookingTasks = []; } if (vaultCookingTasks.find(val => { return val.object_type === cookingTask.object_type && val.object_id === cookingTask.object_id; }) === undefined) { let cookingUrl; if (cookingTask.object_type === 'directory') { cookingUrl = Urls.vault_cook_directory(cookingTask.object_id); } else { cookingUrl = Urls.vault_cook_revision_gitfast(cookingTask.object_id); } if (cookingTask.email) { cookingUrl += '?email=' + cookingTask.email; } - fetch(cookingUrl, {method: 'POST'}) + fetch(cookingUrl, {credentials: 'same-origin', method: 'POST'}) .then(handleFetchError) .then(() => { vaultCookingTasks.push(cookingTask); localStorage.setItem('swh-vault-cooking-tasks', JSON.stringify(vaultCookingTasks)); $('#vault-cook-directory-modal').modal('hide'); $('#vault-cook-revision-modal').modal('hide'); swh.browse.showTab('#vault'); }) .catch(() => { $('#vault-cook-directory-modal').modal('hide'); $('#vault-cook-revision-modal').modal('hide'); }); } else { swh.browse.showTab('#vault'); } } function validateEmail(email) { let re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(String(email).toLowerCase()); } export function cookDirectoryArchive(directoryId) { let email = $('#swh-vault-directory-email').val().trim(); if (!email || validateEmail(email)) { let cookingTask = {'object_type': 'directory', 'object_id': directoryId, 'email': email, 'status': 'new'}; addVaultCookingTask(cookingTask); } else { $('#invalid-email-modal').modal('show'); } } export function cookRevisionArchive(revisionId) { let email = $('#swh-vault-revision-email').val().trim(); if (!email || validateEmail(email)) { let cookingTask = { 'object_type': 'revision', 'object_id': revisionId, 'email': email, 'status': 'new' }; addVaultCookingTask(cookingTask); } else { $('#invalid-email-modal').modal('show'); } } export function initTaskCreationUi() { // reparent the modals to the top navigation div in order to be able // to display them $(document).ready(function() { $('.swh-browse-top-navigation').append($('#vault-cook-directory-modal')); $('.swh-browse-top-navigation').append($('#vault-cook-revision-modal')); $('.swh-browse-top-navigation').append($('#invalid-email-modal')); }); } diff --git a/swh/web/assets/src/bundles/vault/vault-ui.js b/swh/web/assets/src/bundles/vault/vault-ui.js index 214d5729..5a0e4c3f 100644 --- a/swh/web/assets/src/bundles/vault/vault-ui.js +++ b/swh/web/assets/src/bundles/vault/vault-ui.js @@ -1,175 +1,175 @@ import {handleFetchErrors} from 'utils/functions'; let progress = `
;`; let pollingInterval = 5000; let checkVaultId; function updateProgressBar(progressBar, cookingTask) { if (cookingTask.status === 'new') { progressBar.css('background-color', 'rgba(128, 128, 128, 0.5)'); } else if (cookingTask.status === 'pending') { progressBar.css('background-color', 'rgba(0, 0, 255, 0.5)'); } else if (cookingTask.status === 'done') { progressBar.css('background-color', '#5cb85c'); } else if (cookingTask.status === 'failed') { progressBar.css('background-color', 'rgba(255, 0, 0, 0.5)'); progressBar.css('background-image', 'none'); } progressBar.text(cookingTask.progress_message || cookingTask.status); if (cookingTask.status === 'new' || cookingTask.status === 'pending') { progressBar.addClass('active'); } else { progressBar.removeClass('progress-bar-striped'); } } function checkVaultCookingTasks() { let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks')); if (!vaultCookingTasks || vaultCookingTasks.length === 0) { $('.swh-vault-table tbody tr').remove(); checkVaultId = setTimeout(checkVaultCookingTasks, pollingInterval); return; } let cookingTaskRequests = []; let tasks = {}; let currentObjectIds = []; for (let i = 0; i < vaultCookingTasks.length; ++i) { let cookingTask = vaultCookingTasks[i]; currentObjectIds.push(cookingTask.object_id); tasks[cookingTask.object_id] = cookingTask; let cookingUrl; if (cookingTask.object_type === 'directory') { cookingUrl = Urls.vault_cook_directory(cookingTask.object_id); } else { cookingUrl = Urls.vault_cook_revision_gitfast(cookingTask.object_id); } if (cookingTask.status !== 'done' && cookingTask.status !== 'failed') { - cookingTaskRequests.push(fetch(cookingUrl)); + cookingTaskRequests.push(fetch(cookingUrl, {credentials: 'same-origin'})); } } $('.swh-vault-table tbody tr').each((i, row) => { let objectId = $(row).find('.vault-object-id').data('object-id'); if ($.inArray(objectId, currentObjectIds) === -1) { $(row).remove(); } }); Promise.all(cookingTaskRequests) .then(handleFetchErrors) .then(responses => Promise.all(responses.map(r => r.json()))) .then(cookingTasks => { let table = $('#vault-cooking-tasks tbody'); for (let i = 0; i < cookingTasks.length; ++i) { let cookingTask = tasks[cookingTasks[i].obj_id]; cookingTask.status = cookingTasks[i].status; cookingTask.fetch_url = cookingTasks[i].fetch_url; cookingTask.progress_message = cookingTasks[i].progress_message; } for (let i = 0; i < vaultCookingTasks.length; ++i) { let cookingTask = vaultCookingTasks[i]; let rowTask = $('#vault-task-' + cookingTask.object_id); if (!rowTask.length) { let browseUrl; if (cookingTask.object_type === 'directory') { browseUrl = Urls.browse_directory(cookingTask.object_id); } else { browseUrl = Urls.browse_revision(cookingTask.object_id); } let progressBar = $.parseHTML(progress)[0]; let progressBarContent = $(progressBar).find('.progress-bar'); updateProgressBar(progressBarContent, cookingTask); let tableRow; if (cookingTask.object_type === 'directory') { tableRow = ``; } else { tableRow = ``; } tableRow += ''; if (cookingTask.object_type === 'directory') { tableRow += 'directory'; } else { tableRow += 'revision'; } tableRow += `${cookingTask.object_id}`; tableRow += `${progressBar.outerHTML}`; let downloadLink = 'Waiting for download link to be available'; if (cookingTask.status === 'done') { downloadLink = `Download'; } else if (cookingTask.status === 'failed') { downloadLink = ''; } tableRow += `${downloadLink}`; tableRow += ''; table.prepend(tableRow); } else { let progressBar = rowTask.find('.progress-bar'); updateProgressBar(progressBar, cookingTask); let downloadLink = rowTask.find('.vault-dl-link'); if (cookingTask.status === 'done') { downloadLink[0].innerHTML = `Download'; } else if (cookingTask.status === 'failed') { downloadLink[0].innerHTML = ''; } } } localStorage.setItem('swh-vault-cooking-tasks', JSON.stringify(vaultCookingTasks)); checkVaultId = setTimeout(checkVaultCookingTasks, pollingInterval); }) .catch(() => {}); } export function initUi() { $('#vault-tasks-toggle-selection').change(event => { $('.vault-task-toggle-selection').prop('checked', event.currentTarget.checked); }); $('#vault-remove-tasks').click(() => { clearTimeout(checkVaultId); let tasksToRemove = []; $('.swh-vault-table tbody tr').each((i, row) => { let taskSelected = $(row).find('.vault-task-toggle-selection').prop('checked'); if (taskSelected) { let objectId = $(row).find('.vault-object-id').data('object-id'); tasksToRemove.push(objectId); $(row).remove(); } }); let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks')); vaultCookingTasks = $.grep(vaultCookingTasks, task => { return $.inArray(task.object_id, tasksToRemove) === -1; }); localStorage.setItem('swh-vault-cooking-tasks', JSON.stringify(vaultCookingTasks)); $('#vault-tasks-toggle-selection').prop('checked', false); checkVaultId = setTimeout(checkVaultCookingTasks, pollingInterval); }); checkVaultId = setTimeout(checkVaultCookingTasks, pollingInterval); $(document).on('shown.bs.tab', 'a[data-toggle="tab"]', e => { if (e.currentTarget.text.trim() === 'Vault') { clearTimeout(checkVaultId); checkVaultCookingTasks(); } }); window.onfocus = () => { clearTimeout(checkVaultId); checkVaultCookingTasks(); }; } diff --git a/swh/web/assets/src/bundles/webapp/markdown-rendering.js b/swh/web/assets/src/bundles/webapp/markdown-rendering.js index 05881f13..e9ff3cb1 100644 --- a/swh/web/assets/src/bundles/webapp/markdown-rendering.js +++ b/swh/web/assets/src/bundles/webapp/markdown-rendering.js @@ -1,14 +1,14 @@ export async function renderMarkdown(domElt, markdownDocUrl) { let showdown = await import(/* webpackChunkName: "showdown" */ 'utils/showdown'); $(document).ready(() => { let converter = new showdown.Converter({tables: true}); - fetch(markdownDocUrl) + fetch(markdownDocUrl, {credentials: 'same-origin'}) .then(response => response.text()) .then(data => { $(domElt).html(converter.makeHtml(data)); }); }); } diff --git a/swh/web/static/js/browse.js b/swh/web/static/js/browse.js index 7dc258fe..a9263a8d 100644 --- a/swh/web/static/js/browse.js +++ b/swh/web/static/js/browse.js @@ -1,2 +1,2 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.browse=e():(t.swh=t.swh||{},t.swh.browse=e())}(window,function(){return function(t){var e={};function n(s){if(e[s])return e[s].exports;var r=e[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,s){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:s})},n.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/static/",n(n.s=467)}({205:function(t,e,n){"use strict";n.r(e);n(466),n(464),n(462),n(460);function s(t){function e(){$(".swh-releases-switch").parent().removeClass("active"),$(".swh-branches-switch").parent().addClass("active"),$("#swh-tab-releases").removeClass("active"),$("#swh-tab-branches").addClass("active")}function n(){$(".swh-branches-switch").parent().removeClass("active"),$(".swh-releases-switch").parent().addClass("active"),$("#swh-tab-branches").removeClass("active"),$("#swh-tab-releases").addClass("active")}$(document).ready(function(){$(".dropdown-menu a.swh-branches-switch").click(function(t){e(),t.stopPropagation()}),$(".dropdown-menu a.swh-releases-switch").click(function(t){n(),t.stopPropagation()});var s=!1;$("#swh-branches-releases-dd").on("show.bs.dropdown",function(){if(!s){var t=$(".swh-branches-releases").width();$(".swh-branches-releases").width(t+25),s=!0}}),t&&(t.branch?e():n())})}function r(t,e,n){var s=t[e];t[e]=t[n],t[n]=s}var o=n(35),i=void 0,a=15,u=10*a,c=0,l=null,h=!1;function f(){setTimeout(function(){$("#origin-search-results tbody tr").removeAttr("style")})}function d(t,e){var n=e%u;if(t.length>0){$("#swh-origin-search-results").show(),$("#swh-no-origins-found").hide(),$("#origin-search-results tbody tr").remove();for(var s=$("#origin-search-results tbody"),r=function(e){var n=t[e],r="";r+=''+n.type+"";var o=Urls.browse_origin(n.type,n.url);r+=''+o+"",r+='',r+="",s.append(r);var i=Urls.browse_origin_latest_snapshot(n.id);fetch(i).then(function(t){return t.json()}).then(function(t){var e=n.id;$("#visit-status-origin-"+e).children().remove(),t?$("#visit-status-origin-"+e).append(''):$("#visit-status-origin-"+e).append('')})},o=n;o0?$("#origins-prev-results-button").removeClass("disabled"):$("#origins-prev-results-button").addClass("disabled"),h=!1,setTimeout(function(){window.scrollTo(0,0)})}function w(t,e,n,s){i=t;var a=[];!function t(e,n,s){if(1===(s=s||e.length))n(e);else for(var o=1;o<=s;o+=1)t(e,n,s-1),r(e,(s%2?1:o)-1,s-1)}(t.trim().replace(/\s+/g," ").split(" "),function(t){return a.push(t.join(".*"))});var u=a.join("|"),c=Urls.browse_origin_search(u)+"?limit="+e+"&offset="+n+"®exp=true";$(".swh-loading").addClass("show"),fetch(c).then(o.a).then(function(t){return t.json()}).then(function(e){l=e,"undefined"!=typeof Storage&&(sessionStorage.setItem("last-swh-origin-url-patterns",t),sessionStorage.setItem("last-swh-origin-search-results",JSON.stringify(e)),sessionStorage.setItem("last-swh-origin-search-offset",s)),$(".swh-loading").removeClass("show"),d(e,s)}).catch(function(){$(".swh-loading").removeClass("show"),h=!1})}function g(){$(document).ready(function(){if("undefined"!=typeof Storage){i=sessionStorage.getItem("last-swh-origin-url-patterns");var t=sessionStorage.getItem("last-swh-origin-search-results");c=sessionStorage.getItem("last-swh-origin-search-offset"),t&&($("#origins-url-patterns").val(i),c=parseInt(c),d(JSON.parse(t),c))}$("#search_origins").submit(function(t){var e=$("#origins-url-patterns").val();h=!0,w(e,u,c=0,c),t.preventDefault()}),$("#origins-next-results-button").click(function(t){$("#origins-next-results-button").hasClass("disabled")||h||(h=!0,c+=a,l&&c%u!=0?d(l,c):w(i,u,c,c),t.preventDefault())}),$("#origins-prev-results-button").click(function(t){$("#origins-prev-results-button").hasClass("disabled")||h||(h=!0,c-=a,!l||c>0&&(c+a)%u==0?w(i,u,c+a-u,c):d(l,c),t.preventDefault())}),$(document).on("shown.bs.tab",'a[data-toggle="tab"]',function(t){"Search"===t.currentTarget.text.trim()&&f()})})}var p=["#browse","#search","#help","#vault"];function v(){history.replaceState("",document.title,window.location.pathname+window.location.search)}function b(t){$('.navbar-nav.swh-browse-nav a[href="'+t+'"]').tab("show"),window.scrollTo(0,0)}function m(){var t=window.location.hash;t&&-1===p.indexOf(t)||b(t||"#browse")}function y(){$(document).ready(function(){$(".dropdown-submenu a.dropdown-link").on("click",function(t){$(t.target).next("ul").toggle(),t.stopPropagation(),t.preventDefault()}),$(".navbar-nav.swh-browse-nav a").on("shown.bs.tab",function(t){if("#browse"!==t.target.hash.trim())window.location.hash=t.target.hash;else{var e=window.location.hash;-1!==p.indexOf(e)&&v()}m()}),$(window).on("hashchange",function(){m()}),m()})}n.d(e,"initSnapshotNavigation",function(){return s}),n.d(e,"initOriginSearch",function(){return g}),n.d(e,"browseTabsHash",function(){return p}),n.d(e,"removeHash",function(){return v}),n.d(e,"showTab",function(){return b}),n.d(e,"showRequestedTab",function(){return m}),n.d(e,"initMainNavigation",function(){return y})},35:function(t,e,n){"use strict";function s(t){if(!t.ok)throw Error(t.statusText);return t}function r(t){for(var e=0;e0){$("#swh-origin-search-results").show(),$("#swh-no-origins-found").hide(),$("#origin-search-results tbody tr").remove();for(var s=$("#origin-search-results tbody"),r=function(e){var n=t[e],r="";r+=''+n.type+"";var i=Urls.browse_origin(n.type,n.url);r+=''+i+"",r+='',r+="",s.append(r);var o=Urls.browse_origin_latest_snapshot(n.id);fetch(o,{credentials:"same-origin"}).then(function(t){return t.json()}).then(function(t){var e=n.id;$("#visit-status-origin-"+e).children().remove(),t?$("#visit-status-origin-"+e).append(''):$("#visit-status-origin-"+e).append('')})},i=n;i0?$("#origins-prev-results-button").removeClass("disabled"):$("#origins-prev-results-button").addClass("disabled"),h=!1,setTimeout(function(){window.scrollTo(0,0)})}function w(t,e,n,s){o=t;var a=[];!function t(e,n,s){if(1===(s=s||e.length))n(e);else for(var i=1;i<=s;i+=1)t(e,n,s-1),r(e,(s%2?1:i)-1,s-1)}(t.trim().replace(/\s+/g," ").split(" "),function(t){return a.push(t.join(".*"))});var u=a.join("|"),c=Urls.browse_origin_search(u)+"?limit="+e+"&offset="+n+"®exp=true";$(".swh-loading").addClass("show"),fetch(c,{credentials:"same-origin"}).then(i.a).then(function(t){return t.json()}).then(function(e){l=e,"undefined"!=typeof Storage&&(sessionStorage.setItem("last-swh-origin-url-patterns",t),sessionStorage.setItem("last-swh-origin-search-results",JSON.stringify(e)),sessionStorage.setItem("last-swh-origin-search-offset",s)),$(".swh-loading").removeClass("show"),d(e,s)}).catch(function(){$(".swh-loading").removeClass("show"),h=!1})}function g(){$(document).ready(function(){if("undefined"!=typeof Storage){o=sessionStorage.getItem("last-swh-origin-url-patterns");var t=sessionStorage.getItem("last-swh-origin-search-results");c=sessionStorage.getItem("last-swh-origin-search-offset"),t&&($("#origins-url-patterns").val(o),c=parseInt(c),d(JSON.parse(t),c))}$("#search_origins").submit(function(t){var e=$("#origins-url-patterns").val();h=!0,w(e,u,c=0,c),t.preventDefault()}),$("#origins-next-results-button").click(function(t){$("#origins-next-results-button").hasClass("disabled")||h||(h=!0,c+=a,l&&c%u!=0?d(l,c):w(o,u,c,c),t.preventDefault())}),$("#origins-prev-results-button").click(function(t){$("#origins-prev-results-button").hasClass("disabled")||h||(h=!0,c-=a,!l||c>0&&(c+a)%u==0?w(o,u,c+a-u,c):d(l,c),t.preventDefault())}),$(document).on("shown.bs.tab",'a[data-toggle="tab"]',function(t){"Search"===t.currentTarget.text.trim()&&f()})})}var p=["#browse","#search","#help","#vault"];function v(){history.replaceState("",document.title,window.location.pathname+window.location.search)}function b(t){$('.navbar-nav.swh-browse-nav a[href="'+t+'"]').tab("show"),window.scrollTo(0,0)}function m(){var t=window.location.hash;t&&-1===p.indexOf(t)||b(t||"#browse")}function y(){$(document).ready(function(){$(".dropdown-submenu a.dropdown-link").on("click",function(t){$(t.target).next("ul").toggle(),t.stopPropagation(),t.preventDefault()}),$(".navbar-nav.swh-browse-nav a").on("shown.bs.tab",function(t){if("#browse"!==t.target.hash.trim())window.location.hash=t.target.hash;else{var e=window.location.hash;-1!==p.indexOf(e)&&v()}m()}),$(window).on("hashchange",function(){m()}),m()})}n.d(e,"initSnapshotNavigation",function(){return s}),n.d(e,"initOriginSearch",function(){return g}),n.d(e,"browseTabsHash",function(){return p}),n.d(e,"removeHash",function(){return v}),n.d(e,"showTab",function(){return b}),n.d(e,"showRequestedTab",function(){return m}),n.d(e,"initMainNavigation",function(){return y})},35:function(t,e,n){"use strict";function s(t){if(!t.ok)throw Error(t.statusText);return t}function r(t){for(var e=0;e {\n $('.dropdown-menu a.swh-branches-switch').click(e => {\n setBranchesTabActive();\n e.stopPropagation();\n });\n\n $('.dropdown-menu a.swh-releases-switch').click(e => {\n setReleasesTabActive();\n e.stopPropagation();\n });\n\n let dropdownResized = false;\n\n // hack to resize the branches/releases dropdown content,\n // taking icons into account, in order to make the whole names readable\n $('#swh-branches-releases-dd').on('show.bs.dropdown', () => {\n if (dropdownResized) return;\n let dropdownWidth = $('.swh-branches-releases').width();\n $('.swh-branches-releases').width(dropdownWidth + 25);\n dropdownResized = true;\n });\n\n if (snapshotContext) {\n if (snapshotContext.branch) {\n setBranchesTabActive();\n } else {\n setReleasesTabActive();\n }\n }\n\n });\n\n}\n","// http://dsernst.com/2014/12/14/heaps-permutation-algorithm-in-javascript/\n\nfunction swap(array, pos1, pos2) {\n let temp = array[pos1];\n array[pos1] = array[pos2];\n array[pos2] = temp;\n}\n\nexport function heapsPermute(array, output, n) {\n n = n || array.length; // set n default to array.length\n if (n === 1) {\n output(array);\n } else {\n for (let i = 1; i <= n; i += 1) {\n heapsPermute(array, output, n - 1);\n let j;\n if (n % 2) {\n j = 1;\n } else {\n j = i;\n }\n swap(array, j - 1, n - 1); // -1 to account for javascript zero-indexing\n }\n }\n}\n","import {heapsPermute} from 'utils/heaps-permute';\nimport {handleFetchError} from 'utils/functions';\n\nlet originPatterns;\nlet perPage = 15;\nlet limit = perPage * 10;\nlet offset = 0;\nlet currentData = null;\nlet inSearch = false;\n\nfunction fixTableRowsStyle() {\n setTimeout(() => {\n $('#origin-search-results tbody tr').removeAttr('style');\n });\n}\n\nfunction populateOriginSearchResultsTable(data, offset) {\n let localOffset = offset % limit;\n if (data.length > 0) {\n $('#swh-origin-search-results').show();\n $('#swh-no-origins-found').hide();\n $('#origin-search-results tbody tr').remove();\n let table = $('#origin-search-results tbody');\n for (let i = localOffset; i < localOffset + perPage && i < data.length; ++i) {\n let elem = data[i];\n let tableRow = '';\n tableRow += '' + elem.type + '';\n let browseUrl = Urls.browse_origin(elem.type, elem.url);\n tableRow += '' + browseUrl + '';\n tableRow += '';\n tableRow += '';\n table.append(tableRow);\n // get async latest visit snapshot and update visit status icon\n let latestSnapshotUrl = Urls.browse_origin_latest_snapshot(elem.id);\n fetch(latestSnapshotUrl)\n .then(response => response.json())\n .then(data => {\n let originId = elem.id;\n $('#visit-status-origin-' + originId).children().remove();\n if (data) {\n $('#visit-status-origin-' + originId).append('');\n } else {\n $('#visit-status-origin-' + originId).append('');\n }\n });\n }\n fixTableRowsStyle();\n } else {\n $('#swh-origin-search-results').hide();\n $('#swh-no-origins-found').show();\n }\n if (data.length - localOffset < perPage ||\n (data.length < limit && (localOffset + perPage) === data.length)) {\n $('#origins-next-results-button').addClass('disabled');\n } else {\n $('#origins-next-results-button').removeClass('disabled');\n }\n if (offset > 0) {\n $('#origins-prev-results-button').removeClass('disabled');\n } else {\n $('#origins-prev-results-button').addClass('disabled');\n }\n inSearch = false;\n setTimeout(() => {\n window.scrollTo(0, 0);\n });\n}\n\nfunction searchOrigins(patterns, limit, searchOffset, offset) {\n originPatterns = patterns;\n let patternsArray = patterns.trim().replace(/\\s+/g, ' ').split(' ');\n let patternsPermut = [];\n heapsPermute(patternsArray, p => patternsPermut.push(p.join('.*')));\n let regex = patternsPermut.join('|');\n let searchUrl = Urls.browse_origin_search(regex) + `?limit=${limit}&offset=${searchOffset}®exp=true`;\n\n $('.swh-loading').addClass('show');\n fetch(searchUrl)\n .then(handleFetchError)\n .then(response => response.json())\n .then(data => {\n currentData = data;\n if (typeof Storage !== 'undefined') {\n sessionStorage.setItem('last-swh-origin-url-patterns', patterns);\n sessionStorage.setItem('last-swh-origin-search-results', JSON.stringify(data));\n sessionStorage.setItem('last-swh-origin-search-offset', offset);\n }\n $('.swh-loading').removeClass('show');\n populateOriginSearchResultsTable(data, offset);\n })\n .catch(() => {\n $('.swh-loading').removeClass('show');\n inSearch = false;\n });\n}\n\nexport function initOriginSearch() {\n $(document).ready(() => {\n if (typeof Storage !== 'undefined') {\n originPatterns = sessionStorage.getItem('last-swh-origin-url-patterns');\n let data = sessionStorage.getItem('last-swh-origin-search-results');\n offset = sessionStorage.getItem('last-swh-origin-search-offset');\n if (data) {\n $('#origins-url-patterns').val(originPatterns);\n offset = parseInt(offset);\n populateOriginSearchResultsTable(JSON.parse(data), offset);\n }\n }\n\n $('#search_origins').submit(event => {\n let patterns = $('#origins-url-patterns').val();\n offset = 0;\n inSearch = true;\n searchOrigins(patterns, limit, offset, offset);\n event.preventDefault();\n });\n\n $('#origins-next-results-button').click(event => {\n if ($('#origins-next-results-button').hasClass('disabled') || inSearch) {\n return;\n }\n inSearch = true;\n offset += perPage;\n if (!currentData || offset % limit === 0) {\n searchOrigins(originPatterns, limit, offset, offset);\n } else {\n populateOriginSearchResultsTable(currentData, offset);\n }\n event.preventDefault();\n });\n\n $('#origins-prev-results-button').click(event => {\n if ($('#origins-prev-results-button').hasClass('disabled') || inSearch) {\n return;\n }\n inSearch = true;\n offset -= perPage;\n if (!currentData || (offset > 0 && (offset + perPage) % limit === 0)) {\n searchOrigins(originPatterns, limit, (offset + perPage) - limit, offset);\n } else {\n populateOriginSearchResultsTable(currentData, offset);\n }\n event.preventDefault();\n });\n\n $(document).on('shown.bs.tab', 'a[data-toggle=\"tab\"]', e => {\n if (e.currentTarget.text.trim() === 'Search') {\n fixTableRowsStyle();\n }\n });\n });\n}\n","export let browseTabsHash = ['#browse', '#search', '#help', '#vault'];\n\nexport function removeHash() {\n history.replaceState('', document.title, window.location.pathname + window.location.search);\n}\n\nexport function showTab(hash) {\n $('.navbar-nav.swh-browse-nav a[href=\"' + hash + '\"]').tab('show');\n window.scrollTo(0, 0);\n}\n\nexport function showRequestedTab() {\n let hash = window.location.hash;\n if (hash && browseTabsHash.indexOf(hash) === -1) {\n return;\n }\n if (hash) {\n showTab(hash);\n } else {\n showTab('#browse');\n }\n}\n\nexport function initMainNavigation() {\n\n $(document).ready(() => {\n\n $('.dropdown-submenu a.dropdown-link').on('click', e => {\n $(e.target).next('ul').toggle();\n e.stopPropagation();\n e.preventDefault();\n });\n\n // Change hash for page reload\n $('.navbar-nav.swh-browse-nav a').on('shown.bs.tab', e => {\n if (e.target.hash.trim() !== '#browse') {\n window.location.hash = e.target.hash;\n } else {\n let hash = window.location.hash;\n if (browseTabsHash.indexOf(hash) !== -1) {\n removeHash();\n }\n }\n showRequestedTab();\n });\n\n // update displayed tab when the url fragment changes\n $(window).on('hashchange', () => {\n showRequestedTab();\n });\n\n // show requested tab when loading the page\n showRequestedTab();\n });\n\n}\n","// main bundle for the swh-web/browse application\n\nimport './browse.css';\nimport './breadcrumbs.css';\nimport './content.css';\nimport './snapshot-navigation.css';\n\nexport * from './snapshot-navigation';\nexport * from './origin-search';\nexport * from './main-navigation';\n","// utility functions\n\nexport function handleFetchError(response) {\n if (!response.ok) {\n throw Error(response.statusText);\n }\n return response;\n}\n\nexport function handleFetchErrors(responses) {\n for (let i = 0; i < responses.length; ++i) {\n if (!responses[i].ok) {\n throw Error(responses[i].statusText);\n }\n }\n return responses;\n}\n\nexport function staticAsset(asset) {\n return `${__STATIC__}${asset}`;\n}\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack://swh.[name]/webpack/universalModuleDefinition","webpack://swh.[name]/webpack/bootstrap","webpack://swh.[name]/./swh/web/assets/src/bundles/browse/snapshot-navigation.js","webpack://swh.[name]/./swh/web/assets/src/utils/heaps-permute.js","webpack://swh.[name]/./swh/web/assets/src/bundles/browse/origin-search.js","webpack://swh.[name]/./swh/web/assets/src/bundles/browse/main-navigation.js","webpack://swh.[name]/./swh/web/assets/src/bundles/browse/index.js","webpack://swh.[name]/./swh/web/assets/src/utils/functions.js"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","r","value","n","__esModule","object","property","prototype","hasOwnProperty","p","s","initSnapshotNavigation","snapshotContext","setBranchesTabActive","$","parent","removeClass","addClass","setReleasesTabActive","document","ready","click","e","stopPropagation","dropdownResized","on","dropdownWidth","width","branch","swap","array","pos1","pos2","temp","originPatterns","perPage","origin_search_limit","origin_search_offset","currentData","inSearch","fixTableRowsStyle","setTimeout","removeAttr","populateOriginSearchResultsTable","data","offset","localOffset","length","show","hide","remove","table","_loop","elem","tableRow","type","browseUrl","Urls","browse_origin","url","id","append","latestSnapshotUrl","browse_origin_latest_snapshot","fetch","credentials","then","response","json","originId","children","scrollTo","searchOrigins","patterns","limit","searchOffset","patternsPermut","heapsPermute","output","trim","replace","split","push","join","regex","searchUrl","browse_origin_search","functions","Storage","sessionStorage","setItem","JSON","stringify","catch","initOriginSearch","getItem","val","parseInt","parse","submit","event","preventDefault","hasClass","currentTarget","text","browseTabsHash","removeHash","history","replaceState","title","location","pathname","search","showTab","hash","tab","showRequestedTab","indexOf","initMainNavigation","target","next","toggle","__webpack_exports__","handleFetchError","ok","Error","statusText","handleFetchErrors","responses","staticAsset","asset"],"mappings":"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","file":"js/browse.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"browse\"] = factory();\n\telse\n\t\troot[\"swh\"] = root[\"swh\"] || {}, root[\"swh\"][\"browse\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/static/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 467);\n","export function initSnapshotNavigation(snapshotContext) {\n\n function setBranchesTabActive() {\n $('.swh-releases-switch').parent().removeClass('active');\n $('.swh-branches-switch').parent().addClass('active');\n $('#swh-tab-releases').removeClass('active');\n $('#swh-tab-branches').addClass('active');\n }\n\n function setReleasesTabActive() {\n $('.swh-branches-switch').parent().removeClass('active');\n $('.swh-releases-switch').parent().addClass('active');\n $('#swh-tab-branches').removeClass('active');\n $('#swh-tab-releases').addClass('active');\n }\n\n $(document).ready(() => {\n $('.dropdown-menu a.swh-branches-switch').click(e => {\n setBranchesTabActive();\n e.stopPropagation();\n });\n\n $('.dropdown-menu a.swh-releases-switch').click(e => {\n setReleasesTabActive();\n e.stopPropagation();\n });\n\n let dropdownResized = false;\n\n // hack to resize the branches/releases dropdown content,\n // taking icons into account, in order to make the whole names readable\n $('#swh-branches-releases-dd').on('show.bs.dropdown', () => {\n if (dropdownResized) return;\n let dropdownWidth = $('.swh-branches-releases').width();\n $('.swh-branches-releases').width(dropdownWidth + 25);\n dropdownResized = true;\n });\n\n if (snapshotContext) {\n if (snapshotContext.branch) {\n setBranchesTabActive();\n } else {\n setReleasesTabActive();\n }\n }\n\n });\n\n}\n","// http://dsernst.com/2014/12/14/heaps-permutation-algorithm-in-javascript/\n\nfunction swap(array, pos1, pos2) {\n let temp = array[pos1];\n array[pos1] = array[pos2];\n array[pos2] = temp;\n}\n\nexport function heapsPermute(array, output, n) {\n n = n || array.length; // set n default to array.length\n if (n === 1) {\n output(array);\n } else {\n for (let i = 1; i <= n; i += 1) {\n heapsPermute(array, output, n - 1);\n let j;\n if (n % 2) {\n j = 1;\n } else {\n j = i;\n }\n swap(array, j - 1, n - 1); // -1 to account for javascript zero-indexing\n }\n }\n}\n","import {heapsPermute} from 'utils/heaps-permute';\nimport {handleFetchError} from 'utils/functions';\n\nlet originPatterns;\nlet perPage = 15;\nlet limit = perPage * 10;\nlet offset = 0;\nlet currentData = null;\nlet inSearch = false;\n\nfunction fixTableRowsStyle() {\n setTimeout(() => {\n $('#origin-search-results tbody tr').removeAttr('style');\n });\n}\n\nfunction populateOriginSearchResultsTable(data, offset) {\n let localOffset = offset % limit;\n if (data.length > 0) {\n $('#swh-origin-search-results').show();\n $('#swh-no-origins-found').hide();\n $('#origin-search-results tbody tr').remove();\n let table = $('#origin-search-results tbody');\n for (let i = localOffset; i < localOffset + perPage && i < data.length; ++i) {\n let elem = data[i];\n let tableRow = '';\n tableRow += '' + elem.type + '';\n let browseUrl = Urls.browse_origin(elem.type, elem.url);\n tableRow += '' + browseUrl + '';\n tableRow += '';\n tableRow += '';\n table.append(tableRow);\n // get async latest visit snapshot and update visit status icon\n let latestSnapshotUrl = Urls.browse_origin_latest_snapshot(elem.id);\n fetch(latestSnapshotUrl, {credentials: 'same-origin'})\n .then(response => response.json())\n .then(data => {\n let originId = elem.id;\n $('#visit-status-origin-' + originId).children().remove();\n if (data) {\n $('#visit-status-origin-' + originId).append('');\n } else {\n $('#visit-status-origin-' + originId).append('');\n }\n });\n }\n fixTableRowsStyle();\n } else {\n $('#swh-origin-search-results').hide();\n $('#swh-no-origins-found').show();\n }\n if (data.length - localOffset < perPage ||\n (data.length < limit && (localOffset + perPage) === data.length)) {\n $('#origins-next-results-button').addClass('disabled');\n } else {\n $('#origins-next-results-button').removeClass('disabled');\n }\n if (offset > 0) {\n $('#origins-prev-results-button').removeClass('disabled');\n } else {\n $('#origins-prev-results-button').addClass('disabled');\n }\n inSearch = false;\n setTimeout(() => {\n window.scrollTo(0, 0);\n });\n}\n\nfunction searchOrigins(patterns, limit, searchOffset, offset) {\n originPatterns = patterns;\n let patternsArray = patterns.trim().replace(/\\s+/g, ' ').split(' ');\n let patternsPermut = [];\n heapsPermute(patternsArray, p => patternsPermut.push(p.join('.*')));\n let regex = patternsPermut.join('|');\n let searchUrl = Urls.browse_origin_search(regex) + `?limit=${limit}&offset=${searchOffset}®exp=true`;\n\n $('.swh-loading').addClass('show');\n fetch(searchUrl, {credentials: 'same-origin'})\n .then(handleFetchError)\n .then(response => response.json())\n .then(data => {\n currentData = data;\n if (typeof Storage !== 'undefined') {\n sessionStorage.setItem('last-swh-origin-url-patterns', patterns);\n sessionStorage.setItem('last-swh-origin-search-results', JSON.stringify(data));\n sessionStorage.setItem('last-swh-origin-search-offset', offset);\n }\n $('.swh-loading').removeClass('show');\n populateOriginSearchResultsTable(data, offset);\n })\n .catch(() => {\n $('.swh-loading').removeClass('show');\n inSearch = false;\n });\n}\n\nexport function initOriginSearch() {\n $(document).ready(() => {\n if (typeof Storage !== 'undefined') {\n originPatterns = sessionStorage.getItem('last-swh-origin-url-patterns');\n let data = sessionStorage.getItem('last-swh-origin-search-results');\n offset = sessionStorage.getItem('last-swh-origin-search-offset');\n if (data) {\n $('#origins-url-patterns').val(originPatterns);\n offset = parseInt(offset);\n populateOriginSearchResultsTable(JSON.parse(data), offset);\n }\n }\n\n $('#search_origins').submit(event => {\n let patterns = $('#origins-url-patterns').val();\n offset = 0;\n inSearch = true;\n searchOrigins(patterns, limit, offset, offset);\n event.preventDefault();\n });\n\n $('#origins-next-results-button').click(event => {\n if ($('#origins-next-results-button').hasClass('disabled') || inSearch) {\n return;\n }\n inSearch = true;\n offset += perPage;\n if (!currentData || offset % limit === 0) {\n searchOrigins(originPatterns, limit, offset, offset);\n } else {\n populateOriginSearchResultsTable(currentData, offset);\n }\n event.preventDefault();\n });\n\n $('#origins-prev-results-button').click(event => {\n if ($('#origins-prev-results-button').hasClass('disabled') || inSearch) {\n return;\n }\n inSearch = true;\n offset -= perPage;\n if (!currentData || (offset > 0 && (offset + perPage) % limit === 0)) {\n searchOrigins(originPatterns, limit, (offset + perPage) - limit, offset);\n } else {\n populateOriginSearchResultsTable(currentData, offset);\n }\n event.preventDefault();\n });\n\n $(document).on('shown.bs.tab', 'a[data-toggle=\"tab\"]', e => {\n if (e.currentTarget.text.trim() === 'Search') {\n fixTableRowsStyle();\n }\n });\n });\n}\n","export let browseTabsHash = ['#browse', '#search', '#help', '#vault'];\n\nexport function removeHash() {\n history.replaceState('', document.title, window.location.pathname + window.location.search);\n}\n\nexport function showTab(hash) {\n $('.navbar-nav.swh-browse-nav a[href=\"' + hash + '\"]').tab('show');\n window.scrollTo(0, 0);\n}\n\nexport function showRequestedTab() {\n let hash = window.location.hash;\n if (hash && browseTabsHash.indexOf(hash) === -1) {\n return;\n }\n if (hash) {\n showTab(hash);\n } else {\n showTab('#browse');\n }\n}\n\nexport function initMainNavigation() {\n\n $(document).ready(() => {\n\n $('.dropdown-submenu a.dropdown-link').on('click', e => {\n $(e.target).next('ul').toggle();\n e.stopPropagation();\n e.preventDefault();\n });\n\n // Change hash for page reload\n $('.navbar-nav.swh-browse-nav a').on('shown.bs.tab', e => {\n if (e.target.hash.trim() !== '#browse') {\n window.location.hash = e.target.hash;\n } else {\n let hash = window.location.hash;\n if (browseTabsHash.indexOf(hash) !== -1) {\n removeHash();\n }\n }\n showRequestedTab();\n });\n\n // update displayed tab when the url fragment changes\n $(window).on('hashchange', () => {\n showRequestedTab();\n });\n\n // show requested tab when loading the page\n showRequestedTab();\n });\n\n}\n","// main bundle for the swh-web/browse application\n\nimport './browse.css';\nimport './breadcrumbs.css';\nimport './content.css';\nimport './snapshot-navigation.css';\n\nexport * from './snapshot-navigation';\nexport * from './origin-search';\nexport * from './main-navigation';\n","// utility functions\n\nexport function handleFetchError(response) {\n if (!response.ok) {\n throw Error(response.statusText);\n }\n return response;\n}\n\nexport function handleFetchErrors(responses) {\n for (let i = 0; i < responses.length; ++i) {\n if (!responses[i].ok) {\n throw Error(responses[i].statusText);\n }\n }\n return responses;\n}\n\nexport function staticAsset(asset) {\n return `${__STATIC__}${asset}`;\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/swh/web/static/js/revision.js b/swh/web/static/js/revision.js index 5a95970d..672c7bdb 100644 --- a/swh/web/static/js/revision.js +++ b/swh/web/static/js/revision.js @@ -1,9 +1,9 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.revision=e():(t.swh=t.swh||{},t.swh.revision=e())}(window,function(){return function(t){function e(e){for(var n,r,o=e[0],s=e[1],a=0,c=[];am;m++)if((y=e?w(s(d=t[m])[0],d[1]):w(t[m]))===u||y===f)return y}else for(v=g.call(t);!(d=v.next()).done;)if((y=i(v,w,d.value,e))===u||y===f)return y}).BREAK=u,e.RETURN=f},140:function(t,e){t.exports=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t}},141:function(t,e,n){"use strict";var r,i,o,s,a=n(85),c=n(7),u=n(43),f=n(77),l=n(44),h=n(28),p=n(42),d=n(140),v=n(139),y=n(76),g=n(75).set,w=n(134)(),m=n(58),x=n(74),b=n(73),_=c.TypeError,S=c.process,j=c.Promise,T="process"==f(S),$=function(){},k=i=m.f,O=!!function(){try{var t=j.resolve(1),e=(t.constructor={})[n(6)("species")]=function(t){t($,$)};return(T||"function"==typeof PromiseRejectionEvent)&&t.then($)instanceof e}catch(t){}}(),P=function(t){var e;return!(!h(t)||"function"!=typeof(e=t.then))&&e},L=function(t,e){if(!t._n){t._n=!0;var n=t._c;w(function(){for(var r=t._v,i=1==t._s,o=0,s=function(e){var n,o,s,a=i?e.ok:e.fail,c=e.resolve,u=e.reject,f=e.domain;try{a?(i||(2==t._h&&C(t),t._h=1),!0===a?n=r:(f&&f.enter(),n=a(r),f&&(f.exit(),s=!0)),n===e.promise?u(_("Promise-chain cycle")):(o=P(n))?o.call(n,c,u):c(n)):u(r)}catch(t){f&&!s&&f.exit(),u(t)}};n.length>o;)s(n[o++]);t._c=[],t._n=!1,e&&!t._h&&E(t)})}},E=function(t){g.call(c,function(){var e,n,r,i=t._v,o=A(t);if(o&&(e=x(function(){T?S.emit("unhandledRejection",i,t):(n=c.onunhandledrejection)?n({promise:t,reason:i}):(r=c.console)&&r.error&&r.error("Unhandled promise rejection",i)}),t._h=T||A(t)?2:1),t._a=void 0,o&&e.e)throw e.v})},A=function(t){return 1!==t._h&&0===(t._a||t._c).length},C=function(t){g.call(c,function(){var e;T?S.emit("rejectionHandled",t):(e=c.onrejectionhandled)&&e({promise:t,reason:t._v})})},M=function(t){var e=this;e._d||(e._d=!0,(e=e._w||e)._v=t,e._s=2,e._a||(e._a=e._c.slice()),L(e,!0))},R=function(t){var e,n=this;if(!n._d){n._d=!0,n=n._w||n;try{if(n===t)throw _("Promise can't be resolved itself");(e=P(t))?w(function(){var r={_w:n,_d:!1};try{e.call(t,u(R,r,1),u(M,r,1))}catch(t){M.call(r,t)}}):(n._v=t,n._s=1,L(n,!1))}catch(t){M.call({_w:n,_d:!1},t)}}};O||(j=function(t){d(this,j,"Promise","_h"),p(t),r.call(this);try{t(u(R,this,1),u(M,this,1))}catch(t){M.call(this,t)}},(r=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1}).prototype=n(133)(j.prototype,{then:function(t,e){var n=k(y(this,j));return n.ok="function"!=typeof t||t,n.fail="function"==typeof e&&e,n.domain=T?S.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&L(this,!1),n.promise},catch:function(t){return this.then(void 0,t)}}),o=function(){var t=new r;this.promise=t,this.resolve=u(R,t,1),this.reject=u(M,t,1)},m.f=k=function(t){return t===j||t===s?new o(t):i(t)}),l(l.G+l.W+l.F*!O,{Promise:j}),n(59)(j,"Promise"),n(132)("Promise"),s=n(18).Promise,l(l.S+l.F*!O,"Promise",{reject:function(t){var e=k(this);return(0,e.reject)(t),e.promise}}),l(l.S+l.F*(a||!O),"Promise",{resolve:function(t){return b(a&&this===s?j:this,t)}}),l(l.S+l.F*!(O&&n(131)(function(t){j.all(t).catch($)})),"Promise",{all:function(t){var e=this,n=k(e),r=n.resolve,i=n.reject,o=x(function(){var n=[],o=0,s=1;v(t,!1,function(t){var a=o++,c=!1;n.push(void 0),s++,e.resolve(t).then(function(t){c||(c=!0,n[a]=t,--s||r(n))},i)}),--s||r(n)});return o.e&&i(o.v),n.promise},race:function(t){var e=this,n=k(e),r=n.reject,i=x(function(){v(t,!1,function(t){e.resolve(t).then(n.resolve,r)})});return i.e&&r(i.v),n.promise}})},142:function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},143:function(t,e){t.exports=function(){}},144:function(t,e,n){"use strict";var r=n(143),i=n(142),o=n(26),s=n(61);t.exports=n(86)(Array,"Array",function(t,e){this._t=s(t),this._i=0,this._k=e},function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,i(1)):i(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])},"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},145:function(t,e,n){n(144);for(var r=n(7),i=n(17),o=n(26),s=n(6)("toStringTag"),a="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),c=0;cf;)if((a=c[f++])!=a)return!0}else for(;u>f;f++)if((t||f in c)&&c[f]===n)return t||f||0;return!t&&-1}}},150:function(t,e,n){var r=n(39);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},151:function(t,e,n){var r=n(40),i=n(61),o=n(149)(!1),s=n(60)("IE_PROTO");t.exports=function(t,e){var n,a=i(t),c=0,u=[];for(n in a)n!=s&&r(a,n)&&u.push(n);for(;e.length>c;)r(a,n=e[c++])&&(~o(u,n)||u.push(n));return u}},152:function(t,e,n){var r=n(151),i=n(79);t.exports=Object.keys||function(t){return r(t,i)}},153:function(t,e,n){var r=n(41),i=n(13),o=n(152);t.exports=n(27)?Object.defineProperties:function(t,e){i(t);for(var n,s=o(e),a=s.length,c=0;a>c;)r.f(t,n=s[c++],e[n]);return t}},154:function(t,e,n){var r=n(13),i=n(153),o=n(79),s=n(60)("IE_PROTO"),a=function(){},c=function(){var t,e=n(62)("iframe"),r=o.length;for(e.style.display="none",n(78).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write("