Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F8395596
webapp-utils.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
13 KB
Subscribers
None
webapp-utils.js
View Options
/**
* Copyright (C) 2018-2021 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
*/
import
objectFitImages
from
'object-fit-images'
;
import
{
selectText
}
from
'utils/functions'
;
import
{
BREAKPOINT_MD
}
from
'utils/constants'
;
let
collapseSidebar
=
false
;
const
previousSidebarState
=
localStorage
.
getItem
(
'remember.lte.pushmenu'
);
if
(
previousSidebarState
!==
undefined
)
{
collapseSidebar
=
previousSidebarState
===
'sidebar-collapse'
;
}
$
(
document
).
on
(
'DOMContentLoaded'
,
()
=>
{
// set state to collapsed on smaller devices
if
(
$
(
window
).
width
()
<
BREAKPOINT_MD
)
{
collapseSidebar
=
true
;
}
// restore previous sidebar state (collapsed/expanded)
if
(
collapseSidebar
)
{
// hack to avoid animated transition for collapsing sidebar
// when loading a page
const
sidebarTransition
=
$
(
'.main-sidebar, .main-sidebar:before'
).
css
(
'transition'
);
const
sidebarEltsTransition
=
$
(
'.sidebar .nav-link p, .main-sidebar .brand-text, .sidebar .user-panel .info'
).
css
(
'transition'
);
$
(
'.main-sidebar, .main-sidebar:before'
).
css
(
'transition'
,
'none'
);
$
(
'.sidebar .nav-link p, .main-sidebar .brand-text, .sidebar .user-panel .info'
).
css
(
'transition'
,
'none'
);
$
(
'body'
).
addClass
(
'sidebar-collapse'
);
$
(
'.swh-words-logo-swh'
).
css
(
'visibility'
,
'visible'
);
// restore transitions for user navigation
setTimeout
(()
=>
{
$
(
'.main-sidebar, .main-sidebar:before'
).
css
(
'transition'
,
sidebarTransition
);
$
(
'.sidebar .nav-link p, .main-sidebar .brand-text, .sidebar .user-panel .info'
).
css
(
'transition'
,
sidebarEltsTransition
);
});
}
});
$
(
document
).
on
(
'collapsed.lte.pushmenu'
,
event
=>
{
if
(
$
(
'body'
).
width
()
>=
BREAKPOINT_MD
)
{
$
(
'.swh-words-logo-swh'
).
css
(
'visibility'
,
'visible'
);
}
});
$
(
document
).
on
(
'shown.lte.pushmenu'
,
event
=>
{
$
(
'.swh-words-logo-swh'
).
css
(
'visibility'
,
'hidden'
);
});
function
ensureNoFooterOverflow
()
{
$
(
'body'
).
css
(
'padding-bottom'
,
$
(
'footer'
).
outerHeight
()
+
'px'
);
}
$
(
document
).
ready
(()
=>
{
// redirect to last browse page if any when clicking on the 'Browse' entry
// in the sidebar
$
(
`.swh-browse-link`
).
click
(
event
=>
{
const
lastBrowsePage
=
sessionStorage
.
getItem
(
'last-browse-page'
);
if
(
lastBrowsePage
)
{
event
.
preventDefault
();
window
.
location
=
lastBrowsePage
;
}
});
const
mainSideBar
=
$
(
'.main-sidebar'
);
function
updateSidebarState
()
{
const
body
=
$
(
'body'
);
if
(
body
.
hasClass
(
'sidebar-collapse'
)
&&
!
mainSideBar
.
hasClass
(
'swh-sidebar-collapsed'
))
{
mainSideBar
.
removeClass
(
'swh-sidebar-expanded'
);
mainSideBar
.
addClass
(
'swh-sidebar-collapsed'
);
$
(
'.swh-words-logo-swh'
).
css
(
'visibility'
,
'visible'
);
}
else
if
(
!
body
.
hasClass
(
'sidebar-collapse'
)
&&
!
mainSideBar
.
hasClass
(
'swh-sidebar-expanded'
))
{
mainSideBar
.
removeClass
(
'swh-sidebar-collapsed'
);
mainSideBar
.
addClass
(
'swh-sidebar-expanded'
);
$
(
'.swh-words-logo-swh'
).
css
(
'visibility'
,
'hidden'
);
}
// ensure correct sidebar state when loading a page
if
(
body
.
hasClass
(
'hold-transition'
))
{
setTimeout
(()
=>
{
updateSidebarState
();
});
}
}
// set sidebar state after collapse / expand animation
mainSideBar
.
on
(
'transitionend'
,
evt
=>
{
updateSidebarState
();
});
updateSidebarState
();
// ensure footer do not overflow main content for mobile devices
// or after resizing the browser window
ensureNoFooterOverflow
();
$
(
window
).
resize
(
function
()
{
ensureNoFooterOverflow
();
if
(
$
(
'body'
).
hasClass
(
'sidebar-collapse'
)
&&
$
(
'body'
).
width
()
>=
BREAKPOINT_MD
)
{
$
(
'.swh-words-logo-swh'
).
css
(
'visibility'
,
'visible'
);
}
});
// activate css polyfill 'object-fit: contain' in old browsers
objectFitImages
();
// reparent the modals to the top navigation div in order to be able
// to display them
$
(
'.swh-browse-top-navigation'
).
append
(
$
(
'.modal'
));
let
selectedCode
=
null
;
function
getCodeOrPreEltUnderPointer
(
e
)
{
const
elts
=
document
.
elementsFromPoint
(
e
.
clientX
,
e
.
clientY
);
for
(
const
elt
of
elts
)
{
if
(
elt
.
nodeName
===
'CODE'
||
elt
.
nodeName
===
'PRE'
)
{
return
elt
;
}
}
return
null
;
}
// click handler to set focus on code block for copy
$
(
document
).
click
(
e
=>
{
selectedCode
=
getCodeOrPreEltUnderPointer
(
e
);
});
function
selectCode
(
event
,
selectedCode
)
{
if
(
selectedCode
)
{
const
hljsLnCodeElts
=
$
(
selectedCode
).
find
(
'.hljs-ln-code'
);
if
(
hljsLnCodeElts
.
length
)
{
selectText
(
hljsLnCodeElts
[
0
],
hljsLnCodeElts
[
hljsLnCodeElts
.
length
-
1
]);
}
else
{
selectText
(
selectedCode
.
firstChild
,
selectedCode
.
lastChild
);
}
event
.
preventDefault
();
}
}
// select the whole text of focused code block when user
// double clicks or hits Ctrl+A
$
(
document
).
dblclick
(
e
=>
{
if
((
e
.
ctrlKey
||
e
.
metaKey
))
{
selectCode
(
e
,
getCodeOrPreEltUnderPointer
(
e
));
}
});
$
(
document
).
keydown
(
e
=>
{
if
((
e
.
ctrlKey
||
e
.
metaKey
)
&&
e
.
key
===
'a'
)
{
selectCode
(
e
,
selectedCode
);
}
});
// show/hide back-to-top button
let
scrollThreshold
=
0
;
scrollThreshold
+=
$
(
'.swh-top-bar'
).
height
()
||
0
;
scrollThreshold
+=
$
(
'.navbar'
).
height
()
||
0
;
$
(
window
).
scroll
(()
=>
{
if
(
$
(
window
).
scrollTop
()
>
scrollThreshold
)
{
$
(
'#back-to-top'
).
css
(
'display'
,
'block'
);
}
else
{
$
(
'#back-to-top'
).
css
(
'display'
,
'none'
);
}
});
// navbar search form submission callback
$
(
'#swh-origins-search-top'
).
submit
(
event
=>
{
event
.
preventDefault
();
if
(
event
.
target
.
checkValidity
())
{
$
(
event
.
target
).
removeClass
(
'was-validated'
);
const
searchQueryText
=
$
(
'#swh-origins-search-top-input'
).
val
().
trim
();
const
queryParameters
=
new
URLSearchParams
();
queryParameters
.
append
(
'q'
,
searchQueryText
);
queryParameters
.
append
(
'with_visit'
,
true
);
queryParameters
.
append
(
'with_content'
,
true
);
window
.
location
=
`
${
Urls
.
browse_search
()
}
?
${
queryParameters
.
toString
()
}
`
;
}
else
{
$
(
event
.
target
).
addClass
(
'was-validated'
);
}
});
});
export
function
initPage
(
page
)
{
$
(
document
).
ready
(()
=>
{
// set relevant sidebar link to page active
$
(
`.swh-
${
page
}
-item`
).
addClass
(
'active'
);
$
(
`.swh-
${
page
}
-link`
).
addClass
(
'active'
);
// triggered when unloading the current page
$
(
window
).
on
(
'unload'
,
()
=>
{
// backup current browse page
if
(
page
===
'browse'
)
{
sessionStorage
.
setItem
(
'last-browse-page'
,
window
.
location
);
}
});
});
}
export
function
initHomePage
()
{
$
(
document
).
ready
(
async
()
=>
{
$
(
'.swh-coverage-list'
).
iFrameResize
({
heightCalculationMethod
:
'taggedElement'
});
const
response
=
await
fetch
(
Urls
.
stat_counters
());
const
data
=
await
response
.
json
();
if
(
data
.
stat_counters
&&
!
$
.
isEmptyObject
(
data
.
stat_counters
))
{
for
(
const
objectType
of
[
'content'
,
'revision'
,
'origin'
,
'directory'
,
'person'
,
'release'
])
{
const
count
=
data
.
stat_counters
[
objectType
];
if
(
count
!==
undefined
)
{
$
(
`#swh-
${
objectType
}
-count`
).
html
(
count
.
toLocaleString
());
}
else
{
$
(
`#swh-
${
objectType
}
-count`
).
closest
(
'.swh-counter-container'
).
hide
();
}
}
}
else
{
$
(
'.swh-counter'
).
html
(
'0'
);
}
if
(
data
.
stat_counters_history
&&
!
$
.
isEmptyObject
(
data
.
stat_counters_history
))
{
for
(
const
objectType
of
[
'content'
,
'revision'
,
'origin'
])
{
const
history
=
data
.
stat_counters_history
[
objectType
];
if
(
history
)
{
swh
.
webapp
.
drawHistoryCounterGraph
(
`#swh-
${
objectType
}
-count-history`
,
history
);
}
else
{
$
(
`#swh-
${
objectType
}
-count-history`
).
hide
();
}
}
}
else
{
$
(
'.swh-counter-history'
).
hide
();
}
});
initPage
(
'home'
);
}
export
function
showModalMessage
(
title
,
message
)
{
$
(
'#swh-web-modal-message .modal-title'
).
text
(
title
);
$
(
'#swh-web-modal-message .modal-content p'
).
text
(
message
);
$
(
'#swh-web-modal-message'
).
modal
(
'show'
);
}
export
function
showModalConfirm
(
title
,
message
,
callback
)
{
$
(
'#swh-web-modal-confirm .modal-title'
).
text
(
title
);
$
(
'#swh-web-modal-confirm .modal-content p'
).
text
(
message
);
$
(
'#swh-web-modal-confirm #swh-web-modal-confirm-ok-btn'
).
bind
(
'click'
,
()
=>
{
callback
();
$
(
'#swh-web-modal-confirm'
).
modal
(
'hide'
);
$
(
'#swh-web-modal-confirm #swh-web-modal-confirm-ok-btn'
).
unbind
(
'click'
);
});
$
(
'#swh-web-modal-confirm'
).
modal
(
'show'
);
}
export
function
showModalHtml
(
title
,
html
,
width
=
'500px'
)
{
$
(
'#swh-web-modal-html .modal-title'
).
text
(
title
);
$
(
'#swh-web-modal-html .modal-body'
).
html
(
html
);
$
(
'#swh-web-modal-html .modal-dialog'
).
css
(
'max-width'
,
width
);
$
(
'#swh-web-modal-html .modal-dialog'
).
css
(
'width'
,
width
);
$
(
'#swh-web-modal-html'
).
modal
(
'show'
);
}
export
function
addJumpToPagePopoverToDataTable
(
dataTableElt
)
{
dataTableElt
.
on
(
'draw.dt'
,
function
()
{
$
(
'.paginate_button.disabled'
).
css
(
'cursor'
,
'pointer'
);
$
(
'.paginate_button.disabled'
).
on
(
'click'
,
event
=>
{
const
pageInfo
=
dataTableElt
.
page
.
info
();
let
content
=
'<select class="jump-to-page">'
;
for
(
let
i
=
1
;
i
<=
pageInfo
.
pages
;
++
i
)
{
let
selected
=
''
;
if
(
i
===
pageInfo
.
page
+
1
)
{
selected
=
'selected'
;
}
content
+=
`<option value="
${
i
}
"
${
selected
}
>
${
i
}
</option>`
;
}
content
+=
`</select><span> /
${
pageInfo
.
pages
}
</span>`
;
$
(
event
.
target
).
popover
({
'title'
:
'Jump to page'
,
'content'
:
content
,
'html'
:
true
,
'placement'
:
'top'
,
'sanitizeFn'
:
swh
.
webapp
.
filterXSS
});
$
(
event
.
target
).
popover
(
'show'
);
$
(
'.jump-to-page'
).
on
(
'change'
,
function
()
{
$
(
'.paginate_button.disabled'
).
popover
(
'hide'
);
const
pageNumber
=
parseInt
(
$
(
this
).
val
())
-
1
;
dataTableElt
.
page
(
pageNumber
).
draw
(
'page'
);
});
});
});
dataTableElt
.
on
(
'preXhr.dt'
,
()
=>
{
$
(
'.paginate_button.disabled'
).
popover
(
'hide'
);
});
}
let
swhObjectIcons
;
export
function
setSwhObjectIcons
(
icons
)
{
swhObjectIcons
=
icons
;
}
export
function
getSwhObjectIcon
(
swhObjectType
)
{
return
swhObjectIcons
[
swhObjectType
];
}
let
browsedSwhObjectMetadata
=
{};
export
function
setBrowsedSwhObjectMetadata
(
metadata
)
{
browsedSwhObjectMetadata
=
metadata
;
}
export
function
getBrowsedSwhObjectMetadata
()
{
return
browsedSwhObjectMetadata
;
}
// This will contain a mapping between an archived object type
// and its related SWHID metadata for each object reachable from
// the current browse view.
// SWHID metadata contain the following keys:
// * object_type: type of archived object
// * object_id: sha1 object identifier
// * swhid: SWHID without contextual info
// * swhid_url: URL to resolve SWHID without contextual info
// * context: object describing SWHID context
// * swhid_with_context: SWHID with contextual info
// * swhid_with_context_url: URL to resolve SWHID with contextual info
let
swhidsContext_
=
{};
export
function
setSwhIdsContext
(
swhidsContext
)
{
swhidsContext_
=
{};
for
(
const
swhidContext
of
swhidsContext
)
{
swhidsContext_
[
swhidContext
.
object_type
]
=
swhidContext
;
}
}
export
function
getSwhIdsContext
()
{
return
swhidsContext_
;
}
function
setFullWidth
(
fullWidth
)
{
if
(
fullWidth
)
{
$
(
'#swh-web-content'
).
removeClass
(
'container'
);
$
(
'#swh-web-content'
).
addClass
(
'container-fluid'
);
}
else
{
$
(
'#swh-web-content'
).
removeClass
(
'container-fluid'
);
$
(
'#swh-web-content'
).
addClass
(
'container'
);
}
localStorage
.
setItem
(
'swh-web-full-width'
,
JSON
.
stringify
(
fullWidth
));
$
(
'#swh-full-width-switch'
).
prop
(
'checked'
,
fullWidth
);
}
export
function
fullWidthToggled
(
event
)
{
setFullWidth
(
$
(
event
.
target
).
prop
(
'checked'
));
}
export
function
setContainerFullWidth
()
{
const
previousFullWidthState
=
JSON
.
parse
(
localStorage
.
getItem
(
'swh-web-full-width'
));
if
(
previousFullWidthState
!==
null
)
{
setFullWidth
(
previousFullWidthState
);
}
}
function
coreSWHIDIsLowerCase
(
swhid
)
{
const
qualifiersPos
=
swhid
.
indexOf
(
';'
);
let
coreSWHID
=
swhid
;
if
(
qualifiersPos
!==
-
1
)
{
coreSWHID
=
swhid
.
slice
(
0
,
qualifiersPos
);
}
return
coreSWHID
.
toLowerCase
()
===
coreSWHID
;
}
export
async
function
validateSWHIDInput
(
swhidInputElt
)
{
const
swhidInput
=
swhidInputElt
.
value
.
trim
();
let
customValidity
=
''
;
if
(
swhidInput
.
toLowerCase
().
startsWith
(
'swh:'
))
{
if
(
coreSWHIDIsLowerCase
(
swhidInput
))
{
const
resolveSWHIDUrl
=
Urls
.
api_1_resolve_swhid
(
swhidInput
);
const
response
=
await
fetch
(
resolveSWHIDUrl
);
const
responseData
=
await
response
.
json
();
if
(
responseData
.
hasOwnProperty
(
'exception'
))
{
customValidity
=
responseData
.
reason
;
}
}
else
{
const
qualifiersPos
=
swhidInput
.
indexOf
(
';'
);
if
(
qualifiersPos
===
-
1
)
{
customValidity
=
'Invalid SWHID: all characters must be in lowercase. '
;
customValidity
+=
`Valid SWHID is
${
swhidInput
.
toLowerCase
()
}
`
;
}
else
{
customValidity
=
'Invalid SWHID: the core part must be in lowercase. '
;
const
coreSWHID
=
swhidInput
.
slice
(
0
,
qualifiersPos
);
customValidity
+=
`Valid SWHID is
${
swhidInput
.
replace
(
coreSWHID
,
coreSWHID
.
toLowerCase
())
}
`
;
}
}
}
swhidInputElt
.
setCustomValidity
(
customValidity
);
$
(
swhidInputElt
).
siblings
(
'.invalid-feedback'
).
text
(
customValidity
);
}
export
function
isUserLoggedIn
()
{
return
JSON
.
parse
(
$
(
'#swh_user_logged_in'
).
text
());
}
File Metadata
Details
Attached
Mime Type
text/x-java
Expires
Jun 4 2025, 7:41 PM (10 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3399326
Attached To
rDWAPPS Web applications
Event Timeline
Log In to Comment