|
@@ -469,461 +469,3 @@ Object.assign(window.OC, {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-/**
|
|
|
- * Initializes core
|
|
|
- */
|
|
|
-function initCore() {
|
|
|
- /**
|
|
|
- * Disable automatic evaluation of responses for $.ajax() functions (and its
|
|
|
- * higher-level alternatives like $.get() and $.post()).
|
|
|
- *
|
|
|
- * If a response to a $.ajax() request returns a content type of "application/javascript"
|
|
|
- * JQuery would previously execute the response body. This is a pretty unexpected
|
|
|
- * behaviour and can result in a bypass of our Content-Security-Policy as well as
|
|
|
- * multiple unexpected XSS vectors.
|
|
|
- */
|
|
|
- $.ajaxSetup({
|
|
|
- contents: {
|
|
|
- script: false
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- /**
|
|
|
- * Disable execution of eval in jQuery. We do require an allowed eval CSP
|
|
|
- * configuration at the moment for handlebars et al. But for jQuery there is
|
|
|
- * not much of a reason to execute JavaScript directly via eval.
|
|
|
- *
|
|
|
- * This thus mitigates some unexpected XSS vectors.
|
|
|
- */
|
|
|
- jQuery.globalEval = function(){};
|
|
|
-
|
|
|
- /**
|
|
|
- * Set users locale to moment.js as soon as possible
|
|
|
- */
|
|
|
- moment.locale(OC.getLocale());
|
|
|
-
|
|
|
- var userAgent = window.navigator.userAgent;
|
|
|
- var msie = userAgent.indexOf('MSIE ');
|
|
|
- var trident = userAgent.indexOf('Trident/');
|
|
|
- var edge = userAgent.indexOf('Edge/');
|
|
|
-
|
|
|
- if (msie > 0 || trident > 0) {
|
|
|
- // (IE 10 or older) || IE 11
|
|
|
- $('html').addClass('ie');
|
|
|
- } else if (edge > 0) {
|
|
|
- // for edge
|
|
|
- $('html').addClass('edge');
|
|
|
- }
|
|
|
-
|
|
|
- // css variables fallback for IE
|
|
|
- if (msie > 0 || trident > 0 || edge > 0) {
|
|
|
- console.info('Legacy browser detected, applying css vars polyfill')
|
|
|
- cssVars({
|
|
|
- watch: true,
|
|
|
- // set edge < 16 as incompatible
|
|
|
- onlyLegacy: !(/Edge\/([0-9]{2})\./i.test(navigator.userAgent)
|
|
|
- && parseInt(/Edge\/([0-9]{2})\./i.exec(navigator.userAgent)[1]) < 16)
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- $(window).on('unload.main', function() {
|
|
|
- OC._unloadCalled = true;
|
|
|
- });
|
|
|
- $(window).on('beforeunload.main', function() {
|
|
|
- // super-trick thanks to http://stackoverflow.com/a/4651049
|
|
|
- // in case another handler displays a confirmation dialog (ex: navigating away
|
|
|
- // during an upload), there are two possible outcomes: user clicked "ok" or
|
|
|
- // "cancel"
|
|
|
-
|
|
|
- // first timeout handler is called after unload dialog is closed
|
|
|
- setTimeout(function() {
|
|
|
- OC._userIsNavigatingAway = true;
|
|
|
-
|
|
|
- // second timeout event is only called if user cancelled (Chrome),
|
|
|
- // but in other browsers it might still be triggered, so need to
|
|
|
- // set a higher delay...
|
|
|
- setTimeout(function() {
|
|
|
- if (!OC._unloadCalled) {
|
|
|
- OC._userIsNavigatingAway = false;
|
|
|
- }
|
|
|
- }, 10000);
|
|
|
- },1);
|
|
|
- });
|
|
|
- $(document).on('ajaxError.main', function( event, request, settings ) {
|
|
|
- if (settings && settings.allowAuthErrors) {
|
|
|
- return;
|
|
|
- }
|
|
|
- OC._processAjaxError(request);
|
|
|
- });
|
|
|
-
|
|
|
- /**
|
|
|
- * Calls the server periodically to ensure that session and CSRF
|
|
|
- * token doesn't expire
|
|
|
- */
|
|
|
- function initSessionHeartBeat() {
|
|
|
- // interval in seconds
|
|
|
- var interval = NaN;
|
|
|
- if (OC.config.session_lifetime) {
|
|
|
- interval = Math.floor(OC.config.session_lifetime / 2);
|
|
|
- }
|
|
|
- interval = isNaN(interval)? 900: interval;
|
|
|
-
|
|
|
- // minimum one minute
|
|
|
- interval = Math.max(60, interval);
|
|
|
- // max interval in seconds set to 24 hours
|
|
|
- interval = Math.min(24 * 3600, interval);
|
|
|
-
|
|
|
- var url = OC.generateUrl('/csrftoken');
|
|
|
- setInterval(function() {
|
|
|
- $.ajax(url).then(function(resp) {
|
|
|
- oc_requesttoken = resp.token;
|
|
|
- OC.requestToken = resp.token;
|
|
|
- }).fail(function(e) {
|
|
|
- console.error('session heartbeat failed', e);
|
|
|
- });
|
|
|
- }, interval * 1000);
|
|
|
- }
|
|
|
-
|
|
|
- // session heartbeat (defaults to enabled)
|
|
|
- if (typeof(OC.config.session_keepalive) === 'undefined' ||
|
|
|
- !!OC.config.session_keepalive) {
|
|
|
-
|
|
|
- initSessionHeartBeat();
|
|
|
- }
|
|
|
-
|
|
|
- OC.registerMenu($('#expand'), $('#expanddiv'), false, true);
|
|
|
-
|
|
|
- // toggle for menus
|
|
|
- //$(document).on('mouseup.closemenus keyup', function(event) {
|
|
|
- $(document).on('mouseup.closemenus', function(event) {
|
|
|
-
|
|
|
- // allow enter as a trigger
|
|
|
- // if (event.key && event.key !== "Enter") {
|
|
|
- // return;
|
|
|
- // }
|
|
|
-
|
|
|
- var $el = $(event.target);
|
|
|
- if ($el.closest('.menu').length || $el.closest('.menutoggle').length) {
|
|
|
- // don't close when clicking on the menu directly or a menu toggle
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- OC.hideMenus();
|
|
|
- });
|
|
|
-
|
|
|
- /**
|
|
|
- * Set up the main menu toggle to react to media query changes.
|
|
|
- * If the screen is small enough, the main menu becomes a toggle.
|
|
|
- * If the screen is bigger, the main menu is not a toggle any more.
|
|
|
- */
|
|
|
- function setupMainMenu() {
|
|
|
-
|
|
|
- // init the more-apps menu
|
|
|
- OC.registerMenu($('#more-apps > a'), $('#navigation'));
|
|
|
-
|
|
|
- // toggle the navigation
|
|
|
- var $toggle = $('#header .header-appname-container');
|
|
|
- var $navigation = $('#navigation');
|
|
|
- var $appmenu = $('#appmenu');
|
|
|
-
|
|
|
- // init the menu
|
|
|
- OC.registerMenu($toggle, $navigation);
|
|
|
- $toggle.data('oldhref', $toggle.attr('href'));
|
|
|
- $toggle.attr('href', '#');
|
|
|
- $navigation.hide();
|
|
|
-
|
|
|
- // show loading feedback on more apps list
|
|
|
- $navigation.delegate('a', 'click', function(event) {
|
|
|
- var $app = $(event.target);
|
|
|
- if(!$app.is('a')) {
|
|
|
- $app = $app.closest('a');
|
|
|
- }
|
|
|
- if(event.which === 1 && !event.ctrlKey && !event.metaKey) {
|
|
|
- $app.find('svg').remove();
|
|
|
- $app.find('div').remove(); // prevent odd double-clicks
|
|
|
- // no need for theming, loader is already inverted on dark mode
|
|
|
- // but we need it over the primary colour
|
|
|
- $app.prepend($('<div/>').addClass('icon-loading-small'));
|
|
|
- } else {
|
|
|
- // Close navigation when opening app in
|
|
|
- // a new tab
|
|
|
- OC.hideMenus(function(){return false;});
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- $navigation.delegate('a', 'mouseup', function(event) {
|
|
|
- if(event.which === 2) {
|
|
|
- // Close navigation when opening app in
|
|
|
- // a new tab via middle click
|
|
|
- OC.hideMenus(function(){return false;});
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // show loading feedback on visible apps list
|
|
|
- $appmenu.delegate('li:not(#more-apps) > a', 'click', function(event) {
|
|
|
- var $app = $(event.target);
|
|
|
- if(!$app.is('a')) {
|
|
|
- $app = $app.closest('a');
|
|
|
- }
|
|
|
- if(event.which === 1 && !event.ctrlKey && !event.metaKey && $app.parent('#more-apps').length === 0) {
|
|
|
- $app.find('svg').remove();
|
|
|
- $app.find('div').remove(); // prevent odd double-clicks
|
|
|
- $app.prepend($('<div/>').addClass(
|
|
|
- OCA.Theming && OCA.Theming.inverted
|
|
|
- ? 'icon-loading-small'
|
|
|
- : 'icon-loading-small-dark'
|
|
|
- ));
|
|
|
- } else {
|
|
|
- // Close navigation when opening app in
|
|
|
- // a new tab
|
|
|
- OC.hideMenus(function(){return false;});
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- function setupUserMenu() {
|
|
|
- var $menu = $('#header #settings');
|
|
|
-
|
|
|
- // show loading feedback
|
|
|
- $menu.delegate('a', 'click', function(event) {
|
|
|
- var $page = $(event.target);
|
|
|
- if (!$page.is('a')) {
|
|
|
- $page = $page.closest('a');
|
|
|
- }
|
|
|
- if(event.which === 1 && !event.ctrlKey && !event.metaKey) {
|
|
|
- $page.find('img').remove();
|
|
|
- $page.find('div').remove(); // prevent odd double-clicks
|
|
|
- $page.prepend($('<div/>').addClass('icon-loading-small'));
|
|
|
- } else {
|
|
|
- // Close navigation when opening menu entry in
|
|
|
- // a new tab
|
|
|
- OC.hideMenus(function(){return false;});
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- $menu.delegate('a', 'mouseup', function(event) {
|
|
|
- if(event.which === 2) {
|
|
|
- // Close navigation when opening app in
|
|
|
- // a new tab via middle click
|
|
|
- OC.hideMenus(function(){return false;});
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- function setupContactsMenu() {
|
|
|
- new OC.ContactsMenu({
|
|
|
- el: $('#contactsmenu .menu'),
|
|
|
- trigger: $('#contactsmenu .menutoggle')
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- setupMainMenu();
|
|
|
- setupUserMenu();
|
|
|
- setupContactsMenu();
|
|
|
-
|
|
|
- // move triangle of apps dropdown to align with app name triangle
|
|
|
- // 2 is the additional offset between the triangles
|
|
|
- if($('#navigation').length) {
|
|
|
- $('#header #nextcloud + .menutoggle').on('click', function(){
|
|
|
- $('#menu-css-helper').remove();
|
|
|
- var caretPosition = $('.header-appname + .icon-caret').offset().left - 2;
|
|
|
- if(caretPosition > 255) {
|
|
|
- // if the app name is longer than the menu, just put the triangle in the middle
|
|
|
- return;
|
|
|
- } else {
|
|
|
- $('head').append('<style id="menu-css-helper">#navigation:after { left: '+ caretPosition +'px; }</style>');
|
|
|
- }
|
|
|
- });
|
|
|
- $('#header #appmenu .menutoggle').on('click', function() {
|
|
|
- $('#appmenu').toggleClass('menu-open');
|
|
|
- if($('#appmenu').is(':visible')) {
|
|
|
- $('#menu-css-helper').remove();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- var resizeMenu = function() {
|
|
|
- var appList = $('#appmenu li');
|
|
|
- var rightHeaderWidth = $('.header-right').outerWidth();
|
|
|
- var headerWidth = $('header').outerWidth();
|
|
|
- var usePercentualAppMenuLimit = 0.33;
|
|
|
- var minAppsDesktop = 8;
|
|
|
- var availableWidth = headerWidth - $('#nextcloud').outerWidth() - (rightHeaderWidth > 210 ? rightHeaderWidth : 210)
|
|
|
- var isMobile = $(window).width() < 768;
|
|
|
- if (!isMobile) {
|
|
|
- availableWidth = availableWidth * usePercentualAppMenuLimit;
|
|
|
- }
|
|
|
- var appCount = Math.floor((availableWidth / $(appList).width()));
|
|
|
- if (isMobile && appCount > minAppsDesktop) {
|
|
|
- appCount = minAppsDesktop;
|
|
|
- }
|
|
|
- if (!isMobile && appCount < minAppsDesktop) {
|
|
|
- appCount = minAppsDesktop;
|
|
|
- }
|
|
|
-
|
|
|
- // show at least 2 apps in the popover
|
|
|
- if(appList.length-1-appCount >= 1) {
|
|
|
- appCount--;
|
|
|
- }
|
|
|
-
|
|
|
- $('#more-apps a').removeClass('active');
|
|
|
- var lastShownApp;
|
|
|
- for (var k = 0; k < appList.length-1; k++) {
|
|
|
- var name = $(appList[k]).data('id');
|
|
|
- if(k < appCount) {
|
|
|
- $(appList[k]).removeClass('hidden');
|
|
|
- $('#apps li[data-id=' + name + ']').addClass('in-header');
|
|
|
- lastShownApp = appList[k];
|
|
|
- } else {
|
|
|
- $(appList[k]).addClass('hidden');
|
|
|
- $('#apps li[data-id=' + name + ']').removeClass('in-header');
|
|
|
- // move active app to last position if it is active
|
|
|
- if(appCount > 0 && $(appList[k]).children('a').hasClass('active')) {
|
|
|
- $(lastShownApp).addClass('hidden');
|
|
|
- $('#apps li[data-id=' + $(lastShownApp).data('id') + ']').removeClass('in-header');
|
|
|
- $(appList[k]).removeClass('hidden');
|
|
|
- $('#apps li[data-id=' + name + ']').addClass('in-header');
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // show/hide more apps icon
|
|
|
- if($('#apps li:not(.in-header)').length === 0) {
|
|
|
- $('#more-apps').hide();
|
|
|
- $('#navigation').hide();
|
|
|
- } else {
|
|
|
- $('#more-apps').show();
|
|
|
- }
|
|
|
- };
|
|
|
- $(window).resize(resizeMenu);
|
|
|
- setTimeout(resizeMenu, 0);
|
|
|
-
|
|
|
- // just add snapper for logged in users
|
|
|
- // and if the app doesn't handle the nav slider itself
|
|
|
- if($('#app-navigation').length && !$('html').hasClass('lte9')
|
|
|
- && !$('#app-content').hasClass('no-snapper')) {
|
|
|
-
|
|
|
- // App sidebar on mobile
|
|
|
- var snapper = new Snap({
|
|
|
- element: document.getElementById('app-content'),
|
|
|
- disable: 'right',
|
|
|
- maxPosition: 300, // $navigation-width
|
|
|
- minDragDistance: 100
|
|
|
- });
|
|
|
-
|
|
|
- $('#app-content').prepend('<div id="app-navigation-toggle" class="icon-menu" style="display:none;" tabindex="0"></div>');
|
|
|
-
|
|
|
- var toggleSnapperOnButton = function(){
|
|
|
- if(snapper.state().state == 'left'){
|
|
|
- snapper.close();
|
|
|
- } else {
|
|
|
- snapper.open('left');
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- $('#app-navigation-toggle').click(function(){
|
|
|
- toggleSnapperOnButton();
|
|
|
- });
|
|
|
-
|
|
|
- $('#app-navigation-toggle').keypress(function(e) {
|
|
|
- if(e.which == 13) {
|
|
|
- toggleSnapperOnButton();
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // close sidebar when switching navigation entry
|
|
|
- var $appNavigation = $('#app-navigation');
|
|
|
- $appNavigation.delegate('a, :button', 'click', function(event) {
|
|
|
- var $target = $(event.target);
|
|
|
- // don't hide navigation when changing settings or adding things
|
|
|
- if($target.is('.app-navigation-noclose') ||
|
|
|
- $target.closest('.app-navigation-noclose').length) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if($target.is('.app-navigation-entry-utils-menu-button') ||
|
|
|
- $target.closest('.app-navigation-entry-utils-menu-button').length) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if($target.is('.add-new') ||
|
|
|
- $target.closest('.add-new').length) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if($target.is('#app-settings') ||
|
|
|
- $target.closest('#app-settings').length) {
|
|
|
- return;
|
|
|
- }
|
|
|
- snapper.close();
|
|
|
- });
|
|
|
-
|
|
|
- var navigationBarSlideGestureEnabled = false;
|
|
|
- var navigationBarSlideGestureAllowed = true;
|
|
|
- var navigationBarSlideGestureEnablePending = false;
|
|
|
-
|
|
|
- OC.allowNavigationBarSlideGesture = function() {
|
|
|
- navigationBarSlideGestureAllowed = true;
|
|
|
-
|
|
|
- if (navigationBarSlideGestureEnablePending) {
|
|
|
- snapper.enable();
|
|
|
-
|
|
|
- navigationBarSlideGestureEnabled = true;
|
|
|
- navigationBarSlideGestureEnablePending = false;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- OC.disallowNavigationBarSlideGesture = function() {
|
|
|
- navigationBarSlideGestureAllowed = false;
|
|
|
-
|
|
|
- if (navigationBarSlideGestureEnabled) {
|
|
|
- var endCurrentDrag = true;
|
|
|
- snapper.disable(endCurrentDrag);
|
|
|
-
|
|
|
- navigationBarSlideGestureEnabled = false;
|
|
|
- navigationBarSlideGestureEnablePending = true;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var toggleSnapperOnSize = function() {
|
|
|
- if($(window).width() > 768) {
|
|
|
- snapper.close();
|
|
|
- snapper.disable();
|
|
|
-
|
|
|
- navigationBarSlideGestureEnabled = false;
|
|
|
- navigationBarSlideGestureEnablePending = false;
|
|
|
- } else if (navigationBarSlideGestureAllowed) {
|
|
|
- snapper.enable();
|
|
|
-
|
|
|
- navigationBarSlideGestureEnabled = true;
|
|
|
- navigationBarSlideGestureEnablePending = false;
|
|
|
- } else {
|
|
|
- navigationBarSlideGestureEnablePending = true;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- $(window).resize(_.debounce(toggleSnapperOnSize, 250));
|
|
|
-
|
|
|
- // initial call
|
|
|
- toggleSnapperOnSize();
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // Update live timestamps every 30 seconds
|
|
|
- setInterval(function() {
|
|
|
- $('.live-relative-timestamp').each(function() {
|
|
|
- $(this).text(OC.Util.relativeModifiedDate(parseInt($(this).attr('data-timestamp'), 10)));
|
|
|
- });
|
|
|
- }, 30 * 1000);
|
|
|
-
|
|
|
- OC.PasswordConfirmation.init();
|
|
|
-}
|
|
|
-
|
|
|
-$(document).ready(initCore);
|
|
|
-
|
|
|
-/**
|
|
|
-// fallback to hashchange when no history support
|
|
|
-if (window.history.pushState) {
|
|
|
- window.onpopstate = _.bind(OC.Util.History._onPopState, OC.Util.History);
|
|
|
-}
|
|
|
-else {
|
|
|
- $(window).on('hashchange', _.bind(OC.Util.History._onPopState, OC.Util.History));
|
|
|
-}
|
|
|
- */
|