2019-03-19 00:17:53 +01:00
|
|
|
/*
|
|
|
|
===============================================================================
|
2019-03-19 15:04:09 +01:00
|
|
|
This JS file is loaded :
|
2019-03-19 00:17:53 +01:00
|
|
|
- in the YunoHost user portal
|
|
|
|
- on every app page if the app nginx's conf does include the ynh snippet
|
|
|
|
===============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
=====================
|
2014-02-17 13:07:28 +01:00
|
|
|
Utilities
|
2019-03-19 00:17:53 +01:00
|
|
|
=====================
|
|
|
|
*/
|
2014-02-17 13:07:28 +01:00
|
|
|
|
2019-03-19 00:17:53 +01:00
|
|
|
/* Console log fix */
|
2014-02-17 13:07:28 +01:00
|
|
|
if (typeof(console) === 'undefined') {
|
|
|
|
var console = {};
|
|
|
|
console.log = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = function() {};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Array utilities
|
|
|
|
https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-arrays.js
|
|
|
|
-------------------------- */
|
|
|
|
Array.contains = function(needle, haystack) {
|
|
|
|
var i = 0,
|
|
|
|
length = haystack.length;
|
|
|
|
|
|
|
|
for (; i < length; i++) {
|
|
|
|
if (haystack[i] === needle) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
Array.each = function(arrayToParse, callback) {
|
|
|
|
var i = 0,
|
|
|
|
length = arrayToParse.length;
|
|
|
|
for (; i < length; i++) {
|
|
|
|
callback(arrayToParse[i]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* CSS classes utilities
|
|
|
|
https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-classes.js
|
|
|
|
-------------------------- */
|
|
|
|
Element.getClassNames = function(element) {
|
|
|
|
var classNames = [],
|
|
|
|
elementClassName = element.className;
|
|
|
|
if (elementClassName !== '') {
|
|
|
|
elementClassName = elementClassName.replace(/\s+/g, ' ');
|
|
|
|
classNames = elementClassName.split(' ');
|
|
|
|
}
|
|
|
|
return classNames;
|
|
|
|
};
|
|
|
|
Element.hasClass = function(element, className) {
|
|
|
|
if (element.classList) {
|
|
|
|
return element.classList.contains(className);
|
|
|
|
}
|
|
|
|
return Array.contains(className, Element.getClassNames(element));
|
|
|
|
};
|
|
|
|
Element.addClass = function(element, className) {
|
|
|
|
if (element.classList) {
|
|
|
|
element.classList.add(className);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!Element.hasClass(element, className)) {
|
|
|
|
var elementClasses = Element.getClassNames(element);
|
|
|
|
elementClasses.push(className);
|
|
|
|
element.className = elementClasses.join(' ');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Element.removeClass = function(element, className) {
|
|
|
|
if (element.classList) {
|
|
|
|
element.classList.remove(className);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var elementClasses = Element.getClassNames(element);
|
|
|
|
var newElementClasses = [];
|
|
|
|
var i = 0,
|
|
|
|
arLength = elementClasses.length;
|
|
|
|
for (; i < arLength; i++) {
|
|
|
|
if (elementClasses[i] !== className) {
|
|
|
|
newElementClasses.push(elementClasses[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
element.className = newElementClasses.join(' ');
|
|
|
|
};
|
|
|
|
Element.toggleClass = function(element, className) {
|
|
|
|
if (!Element.hasClass(element, className)) {
|
|
|
|
Element.addClass(element, className);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Element.removeClass(element, className);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Add Event
|
|
|
|
https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-events.js
|
|
|
|
-------------------------- */
|
2018-09-03 08:46:00 +02:00
|
|
|
window.addEvent = function(el, eventName, callback, options) {
|
2019-02-22 23:44:04 +01:00
|
|
|
if (el == null) { return; }
|
2014-02-17 13:07:28 +01:00
|
|
|
if (el.addEventListener) {
|
2018-09-03 08:46:00 +02:00
|
|
|
if (!options || typeof(options) !== "object") {
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
options.capture = false;
|
|
|
|
el.addEventListener(eventName, callback, options);
|
2014-02-17 13:07:28 +01:00
|
|
|
}
|
|
|
|
else if (el.attachEvent) {
|
|
|
|
el.attachEvent("on" + eventName, function(e) {
|
|
|
|
return callback.call(el, e);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
window.eventPreventDefault = function(event) {
|
|
|
|
return (event.preventDefault) ? event.preventDefault() : event.returnValue = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-11-19 10:13:02 +01:00
|
|
|
/* Draggable
|
|
|
|
|
|
|
|
Sources :
|
|
|
|
http://jsfiddle.net/5t3Ju/
|
|
|
|
http://stackoverflow.com/questions/9334084/moveable-draggable-div
|
|
|
|
http://jsfiddle.net/tovic/Xcb8d/light/
|
|
|
|
-------------------------- */
|
|
|
|
|
2019-02-22 18:44:08 +01:00
|
|
|
function make_element_draggable(id) {
|
2014-11-19 10:13:02 +01:00
|
|
|
|
|
|
|
// Variables
|
|
|
|
this.elem = document.getElementById(id),
|
|
|
|
this.selected = null, // Selected element
|
|
|
|
this.dragged = false, // Dragging status
|
|
|
|
this.x_pos = 0, this.y_pos = 0, // Stores x & y coordinates of the mouse pointer
|
|
|
|
this.x_elem = 0, this.y_elem = 0; // Stores top, left values (edge) of the element
|
|
|
|
|
2018-05-09 18:34:00 +02:00
|
|
|
var _initDrag = function(e){
|
|
|
|
if (e.type === "touchstart"){
|
|
|
|
x_pos = e.touches[0].clientX;
|
|
|
|
y_pos = e.touches[0].clientY;
|
|
|
|
}
|
|
|
|
|
2014-11-19 10:13:02 +01:00
|
|
|
selected = elem;
|
|
|
|
x_elem = x_pos - selected.offsetLeft;
|
|
|
|
y_elem = y_pos - selected.offsetTop;
|
2019-03-19 15:04:09 +01:00
|
|
|
|
|
|
|
// We add listening event for the iframe itself ...
|
|
|
|
// otherwise dragging the tile on the iframe doesn't
|
|
|
|
// work properly.
|
|
|
|
// We do this at click time to have a better chance
|
|
|
|
// that the iframe's body is indeed loaded ...
|
|
|
|
// (a bit hackish but meh)
|
|
|
|
portalOverlay = document.getElementById("ynh-overlay").contentDocument.body;
|
|
|
|
window.addEvent(portalOverlay, 'mousemove', _onMove);
|
|
|
|
window.addEvent(portalOverlay, 'touchmove', _onMove, {passive: false});
|
2018-05-09 18:34:00 +02:00
|
|
|
};
|
2014-11-19 10:13:02 +01:00
|
|
|
|
2018-05-09 18:34:00 +02:00
|
|
|
var _shutDrag = function(e){
|
|
|
|
selected = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
var _onMove = function(e){
|
2014-11-19 10:13:02 +01:00
|
|
|
// Get position
|
2018-05-09 18:34:00 +02:00
|
|
|
x_pos = document.all ? window.event: e.pageX;
|
|
|
|
y_pos = document.all ? window.event : e.pageY;
|
|
|
|
|
2018-09-03 08:46:00 +02:00
|
|
|
if (e.type === "touchmove") {
|
2018-05-09 18:34:00 +02:00
|
|
|
x_pos = e.touches[0].clientX;
|
|
|
|
y_pos = e.touches[0].clientY;
|
|
|
|
}
|
2014-11-19 10:13:02 +01:00
|
|
|
|
|
|
|
if (selected !== null) {
|
2018-09-03 08:46:00 +02:00
|
|
|
if (e.type === "touchmove"){
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
2014-11-19 10:13:02 +01:00
|
|
|
dragged = true;
|
|
|
|
selected.style.left = (x_pos - x_elem) + 'px';
|
|
|
|
selected.style.top = (y_pos - y_elem) + 'px';
|
|
|
|
}
|
2018-05-09 18:34:00 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 10:35:55 +02:00
|
|
|
// Prevent native D'n'D behavior
|
|
|
|
window.addEvent(elem, 'dragstart', function(e){
|
|
|
|
window.eventPreventDefault(e);
|
|
|
|
});
|
|
|
|
|
2018-05-09 18:34:00 +02:00
|
|
|
// Start dragging
|
|
|
|
window.addEvent(elem, 'mousedown', _initDrag);
|
|
|
|
window.addEvent(elem, 'touchstart', _initDrag);
|
|
|
|
|
|
|
|
// Will be called when user dragging an element
|
|
|
|
window.addEvent(window, 'mousemove', _onMove);
|
2018-09-03 08:46:00 +02:00
|
|
|
window.addEvent(window, 'touchmove', _onMove, {passive: false});
|
2014-11-19 10:13:02 +01:00
|
|
|
|
|
|
|
// Destroy the object when we are done
|
2018-05-09 18:34:00 +02:00
|
|
|
window.addEvent(window, 'mouseup', _shutDrag);
|
|
|
|
window.addEvent(window, 'touchend', _shutDrag);
|
|
|
|
window.addEvent(window, 'touchcancel', _shutDrag);
|
2014-11-19 10:13:02 +01:00
|
|
|
|
|
|
|
// Handle click event
|
|
|
|
window.addEvent(elem, 'click', function(e){
|
|
|
|
// Prevent default event
|
|
|
|
window.eventPreventDefault(e);
|
|
|
|
|
2018-09-01 10:35:55 +02:00
|
|
|
// Do not propagate to other click event if dragged out
|
2014-11-19 10:13:02 +01:00
|
|
|
if (dragged) {
|
|
|
|
e.stopImmediatePropagation();
|
|
|
|
}
|
|
|
|
// Reset dragging status
|
|
|
|
dragged = false;
|
|
|
|
});
|
2019-02-13 19:27:34 +01:00
|
|
|
};
|
2014-11-19 10:13:02 +01:00
|
|
|
|
2014-02-17 13:07:28 +01:00
|
|
|
/* ----------------------------------------------------------
|
|
|
|
Main
|
|
|
|
---------------------------------------------------------- */
|
2019-02-22 23:44:04 +01:00
|
|
|
window.addEvent(document, 'DOMContentLoaded', function() {
|
2019-02-22 20:12:25 +01:00
|
|
|
|
2019-02-22 21:07:12 +01:00
|
|
|
// 3 different cases :
|
|
|
|
// - this script is loaded from inside an app
|
|
|
|
// - this script is loaded inside the portal, inside an iframe/overlay activated by clicking the portal button inside an app
|
|
|
|
// - this script is loaded inside the "regular" portal when going to /yunohost/sso.
|
2014-02-04 21:41:53 +01:00
|
|
|
|
2019-02-22 21:07:12 +01:00
|
|
|
var in_app = ! document.body.classList.contains('ynh-user-portal');
|
2019-02-22 21:30:13 +01:00
|
|
|
var in_overlay_iframe = (window.location != window.parent.location);
|
2019-02-22 21:07:12 +01:00
|
|
|
|
|
|
|
if (in_app)
|
|
|
|
{
|
2019-05-23 11:06:25 +02:00
|
|
|
// Do not load inside an app iframe (Roundcube visualisation panel for example).
|
|
|
|
if (window.frameElement == null) {
|
|
|
|
init_portal_button_and_overlay();
|
|
|
|
}
|
2019-02-22 21:07:12 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
init_portal();
|
|
|
|
if (in_overlay_iframe) { tweak_portal_when_in_iframe(); }
|
|
|
|
}
|
2019-02-22 21:17:45 +01:00
|
|
|
});
|
2019-02-22 21:07:12 +01:00
|
|
|
|
|
|
|
//
|
2019-03-19 00:17:53 +01:00
|
|
|
// This function is called when ynh_portal.js is included in an app
|
2019-02-22 21:07:12 +01:00
|
|
|
//
|
|
|
|
// It will create the small yunohost "portal button" usually in the bottom
|
|
|
|
// right corner and initialize the portal overlay, shown when clicking the
|
|
|
|
// portal button meant to make it easier to switch between apps.
|
|
|
|
//
|
|
|
|
function init_portal_button_and_overlay()
|
|
|
|
{
|
2014-05-20 13:15:12 +02:00
|
|
|
// Set and store meta viewport
|
|
|
|
var meta_viewport = document.querySelector('meta[name="viewport"]');
|
|
|
|
if (meta_viewport === null) {
|
|
|
|
meta_viewport = document.createElement('meta');
|
|
|
|
meta_viewport.setAttribute('name', "viewport");
|
|
|
|
meta_viewport.setAttribute('content', "");
|
|
|
|
document.getElementsByTagName('head')[0].insertBefore(meta_viewport, null);
|
|
|
|
}
|
|
|
|
meta_viewport = document.querySelector('meta[name="viewport"]');
|
|
|
|
meta_viewport_content = meta_viewport.getAttribute('content');
|
|
|
|
|
2019-03-19 15:04:09 +01:00
|
|
|
// Prepare and inject the portal overlay (what is activated when clicking on the portal button)
|
|
|
|
var portalOverlay = document.createElement('iframe');
|
2019-03-19 23:29:46 +01:00
|
|
|
portalOverlay.src = "/yunohost/sso/portal.html";
|
2019-03-19 15:04:09 +01:00
|
|
|
portalOverlay.setAttribute("id","ynh-overlay");
|
2021-05-12 00:50:49 +02:00
|
|
|
portalOverlay.setAttribute("style","display: none;"); // make sure the overlay is invisible already when loading it
|
|
|
|
// portalOverlay.setAttribute("class","ynh-fadeOut"); // set overlay as masked when loading it
|
2019-03-19 15:04:09 +01:00
|
|
|
document.body.insertBefore(portalOverlay, null);
|
|
|
|
|
2019-02-22 18:44:08 +01:00
|
|
|
// Inject portal button
|
|
|
|
var portalButton = document.createElement('a');
|
|
|
|
portalButton.setAttribute('id', 'ynh-overlay-switch');
|
|
|
|
portalButton.setAttribute('href', '/yunohost/sso/');
|
|
|
|
portalButton.setAttribute('class', 'disableAjax');
|
|
|
|
document.body.insertBefore(portalButton, null);
|
|
|
|
// Make portal button draggable, for user convenience
|
|
|
|
make_element_draggable('ynh-overlay-switch');
|
|
|
|
|
|
|
|
// Bind portal button
|
|
|
|
window.addEvent(portalButton, 'click', function(e){
|
|
|
|
// Prevent default click
|
|
|
|
window.eventPreventDefault(e);
|
|
|
|
// Toggle overlay on YNHPortal button click
|
|
|
|
Element.toggleClass(document.querySelector('html'), 'ynh-panel-active');
|
|
|
|
Element.toggleClass(portalOverlay, 'ynh-active');
|
|
|
|
|
2019-04-10 19:25:38 +02:00
|
|
|
if (Element.hasClass(portalOverlay, 'ynh-active')) {
|
2021-05-12 00:50:49 +02:00
|
|
|
portalOverlay.setAttribute("style","display: block;");
|
2019-02-22 18:44:08 +01:00
|
|
|
meta_viewport.setAttribute('content', meta_viewport_content);
|
|
|
|
Element.addClass(portalOverlay, 'ynh-fadeIn');
|
|
|
|
Element.removeClass(portalOverlay, 'ynh-fadeOut');
|
|
|
|
} else {
|
2021-05-12 00:50:49 +02:00
|
|
|
portalOverlay.setAttribute("style","display: none;");
|
2019-02-22 18:44:08 +01:00
|
|
|
meta_viewport.setAttribute('content', "width=device-width");
|
|
|
|
Element.removeClass(portalOverlay, 'ynh-fadeIn');
|
|
|
|
Element.addClass(portalOverlay, 'ynh-fadeOut');
|
2019-02-21 19:29:06 +01:00
|
|
|
}
|
|
|
|
});
|
2019-02-22 21:17:45 +01:00
|
|
|
}
|
2019-02-22 21:07:12 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// This function is called to initialize elements like the app tile colors and other things ...
|
|
|
|
//
|
|
|
|
function init_portal()
|
|
|
|
{
|
2019-02-22 21:17:45 +01:00
|
|
|
|
2019-02-22 23:44:04 +01:00
|
|
|
window.addEvent(document.getElementById('add-mailalias'), "click", function() {
|
2019-02-22 21:17:45 +01:00
|
|
|
// Clone last input.
|
|
|
|
var inputAliasClone = document.querySelector('.mailalias-input').cloneNode(true);
|
|
|
|
// Empty value.
|
|
|
|
inputAliasClone.value = '';
|
|
|
|
// Append to form-group.
|
2019-02-22 23:44:04 +01:00
|
|
|
this.parentNode.insertBefore(inputAliasClone, this);
|
2019-02-22 21:17:45 +01:00
|
|
|
});
|
|
|
|
|
2019-02-22 23:44:04 +01:00
|
|
|
window.addEvent(document.getElementById('add-maildrop'), "click", function() {
|
2019-02-22 21:17:45 +01:00
|
|
|
// Clone last input.
|
|
|
|
var inputDropClone = document.querySelector('.maildrop-input').cloneNode(true);
|
|
|
|
// Empty value.
|
|
|
|
inputDropClone.value = '';
|
|
|
|
// Append to form-group.
|
2019-02-22 23:44:04 +01:00
|
|
|
this.parentNode.insertBefore(inputDropClone, this);
|
2019-02-22 21:17:45 +01:00
|
|
|
});
|
|
|
|
|
2019-02-22 23:44:04 +01:00
|
|
|
Array.each(document.getElementsByClassName("app-tile"), function(el) {
|
2019-02-26 10:52:07 +01:00
|
|
|
// Set first-letter data attribute.
|
|
|
|
el.querySelector('.first-letter').innerHTML = el.getAttribute("data-appname").substring(0, 2);
|
2019-03-19 00:17:53 +01:00
|
|
|
// handle app links so they work both in plain info page and in the info iframe called from ynh_portal.js
|
2019-02-22 23:44:04 +01:00
|
|
|
window.addEvent(el, 'click', function(event) {
|
|
|
|
// if asked to open in new tab
|
|
|
|
if (event.ctrlKey || event.shiftKey || event.metaKey
|
|
|
|
|| (event.button && event.button == 1)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// if asked in current tab
|
|
|
|
else {
|
|
|
|
event.preventDefault();
|
|
|
|
parent.location.href=this.href;
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
});
|
|
|
|
});
|
2019-02-22 21:07:12 +01:00
|
|
|
}
|
|
|
|
|
2021-05-12 00:50:49 +02:00
|
|
|
|
2019-02-22 21:07:12 +01:00
|
|
|
function tweak_portal_when_in_iframe()
|
|
|
|
{
|
2019-02-22 21:17:45 +01:00
|
|
|
// Set class to body to show we're in overlay
|
2019-02-22 21:37:23 +01:00
|
|
|
document.body.classList.add('in_app_overlay');
|
2019-02-22 21:17:45 +01:00
|
|
|
let userContainer = document.querySelector('a.user-container');
|
2019-02-22 23:56:01 +01:00
|
|
|
if (userContainer) {
|
|
|
|
userContainer.classList.replace('user-container-info', 'user-container-edit');
|
|
|
|
userContainer.setAttribute('href', userContainer
|
|
|
|
.getAttribute('href')
|
|
|
|
.replace('edit.html', ''));
|
|
|
|
window.addEvent(userContainer, 'click', function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
window.parent.location.href = userContainer.getAttribute('href');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let logoutButton = document.getElementById('ynh-logout');
|
|
|
|
if (logoutButton)
|
|
|
|
{
|
|
|
|
// We force to do the logout "globally", not just in the
|
|
|
|
// iframe, otherwise after login out the url might still be
|
|
|
|
// domain.tld/app which is weird ...
|
|
|
|
window.addEvent(logoutButton, 'click', function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
window.parent.location.href = logoutButton.getAttribute("href");
|
|
|
|
});
|
|
|
|
}
|
2021-05-12 00:54:32 +02:00
|
|
|
}
|