aboutsummaryrefslogtreecommitdiffstats
path: root/core/src/init.js
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/init.js')
-rw-r--r--core/src/init.js281
1 files changed, 281 insertions, 0 deletions
diff --git a/core/src/init.js b/core/src/init.js
new file mode 100644
index 00000000000..1bcd8218702
--- /dev/null
+++ b/core/src/init.js
@@ -0,0 +1,281 @@
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+/* globals Snap */
+import _ from 'underscore'
+import $ from 'jquery'
+import moment from 'moment'
+
+import OC from './OC/index.js'
+import { initSessionHeartBeat } from './session-heartbeat.ts'
+import { setUp as setUpContactsMenu } from './components/ContactsMenu.js'
+import { setUp as setUpMainMenu } from './components/MainMenu.js'
+import { setUp as setUpUserMenu } from './components/UserMenu.js'
+import { interceptRequests } from './utils/xhr-request.js'
+import { initFallbackClipboardAPI } from './utils/ClipboardFallback.ts'
+
+// keep in sync with core/css/variables.scss
+const breakpointMobileWidth = 1024
+
+const initLiveTimestamps = () => {
+ // Update live timestamps every 30 seconds
+ setInterval(() => {
+ $('.live-relative-timestamp').each(function() {
+ const timestamp = parseInt($(this).attr('data-timestamp'), 10)
+ $(this).text(moment(timestamp).fromNow())
+ })
+ }, 30 * 1000)
+}
+
+/**
+ * Moment doesn't have aliases for every locale and doesn't parse some locale IDs correctly so we need to alias them
+ */
+const localeAliases = {
+ zh: 'zh-cn',
+ zh_Hans: 'zh-cn',
+ zh_Hans_CN: 'zh-cn',
+ zh_Hans_HK: 'zh-cn',
+ zh_Hans_MO: 'zh-cn',
+ zh_Hans_SG: 'zh-cn',
+ zh_Hant: 'zh-hk',
+ zh_Hant_HK: 'zh-hk',
+ zh_Hant_MO: 'zh-mo',
+ zh_Hant_TW: 'zh-tw',
+}
+let locale = OC.getLocale()
+if (Object.prototype.hasOwnProperty.call(localeAliases, locale)) {
+ locale = localeAliases[locale]
+}
+
+/**
+ * Set users locale to moment.js as soon as possible
+ */
+moment.locale(locale)
+
+/**
+ * Initializes core
+ */
+export const initCore = () => {
+ interceptRequests()
+ initFallbackClipboardAPI()
+
+ $(window).on('unload.main', () => { OC._unloadCalled = true })
+ $(window).on('beforeunload.main', () => {
+ // 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(() => {
+ 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(() => {
+ if (!OC._unloadCalled) {
+ OC._userIsNavigatingAway = false
+ }
+ }, 10000)
+ }, 1)
+ })
+ $(document).on('ajaxError.main', function(event, request, settings) {
+ if (settings && settings.allowAuthErrors) {
+ return
+ }
+ OC._processAjaxError(request)
+ })
+
+ initSessionHeartBeat()
+
+ OC.registerMenu($('#expand'), $('#expanddiv'), false, true)
+
+ // toggle for menus
+ $(document).on('mouseup.closemenus', event => {
+ const $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()
+ })
+
+ setUpMainMenu()
+ setUpUserMenu()
+ setUpContactsMenu()
+
+ // 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
+ const 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>')
+
+ // keep track whether snapper is currently animating, and
+ // prevent to call open or close while that is the case
+ // to avoid duplicating events (snap.js doesn't check this)
+ let animating = false
+ snapper.on('animating', () => {
+ // we need this because the trigger button
+ // is also implicitly wired to close by snapper
+ animating = true
+ })
+ snapper.on('animated', () => {
+ animating = false
+ })
+ snapper.on('start', () => {
+ // we need this because dragging triggers that
+ animating = true
+ })
+ snapper.on('end', () => {
+ // we need this because dragging stop triggers that
+ animating = false
+ })
+ snapper.on('open', () => {
+ $appNavigation.attr('aria-hidden', 'false')
+ })
+ snapper.on('close', () => {
+ $appNavigation.attr('aria-hidden', 'true')
+ })
+
+ // These are necessary because calling open or close
+ // on snapper during an animation makes it trigger an
+ // unfinishable animation, which itself will continue
+ // triggering animating events and cause high CPU load,
+ //
+ // Ref https://github.com/jakiestfu/Snap.js/issues/216
+ const oldSnapperOpen = snapper.open
+ const oldSnapperClose = snapper.close
+ const _snapperOpen = () => {
+ if (animating || snapper.state().state !== 'closed') {
+ return
+ }
+ oldSnapperOpen('left')
+ }
+
+ const _snapperClose = () => {
+ if (animating || snapper.state().state === 'closed') {
+ return
+ }
+ oldSnapperClose()
+ }
+
+ // Needs to be deferred to properly catch in-between
+ // events that snap.js is triggering after dragging.
+ //
+ // Skipped when running unit tests as we are not testing
+ // the snap.js workarounds...
+ if (!window.TESTING) {
+ snapper.open = () => {
+ _.defer(_snapperOpen)
+ }
+ snapper.close = () => {
+ _.defer(_snapperClose)
+ }
+ }
+
+ $('#app-navigation-toggle').click((e) => {
+ // close is implicit in the button by snap.js
+ if (snapper.state().state !== 'left') {
+ snapper.open()
+ }
+ })
+ $('#app-navigation-toggle').keypress(e => {
+ if (snapper.state().state === 'left') {
+ snapper.close()
+ } else {
+ snapper.open()
+ }
+ })
+
+ // close sidebar when switching navigation entry
+ const $appNavigation = $('#app-navigation')
+ $appNavigation.attr('aria-hidden', 'true')
+ $appNavigation.delegate('a, :button', 'click', event => {
+ const $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()
+ })
+
+ let navigationBarSlideGestureEnabled = false
+ let navigationBarSlideGestureAllowed = true
+ let navigationBarSlideGestureEnablePending = false
+
+ OC.allowNavigationBarSlideGesture = () => {
+ navigationBarSlideGestureAllowed = true
+
+ if (navigationBarSlideGestureEnablePending) {
+ snapper.enable()
+
+ navigationBarSlideGestureEnabled = true
+ navigationBarSlideGestureEnablePending = false
+ }
+ }
+
+ OC.disallowNavigationBarSlideGesture = () => {
+ navigationBarSlideGestureAllowed = false
+
+ if (navigationBarSlideGestureEnabled) {
+ const endCurrentDrag = true
+ snapper.disable(endCurrentDrag)
+
+ navigationBarSlideGestureEnabled = false
+ navigationBarSlideGestureEnablePending = true
+ }
+ }
+
+ const toggleSnapperOnSize = () => {
+ if ($(window).width() > breakpointMobileWidth) {
+ $appNavigation.attr('aria-hidden', 'false')
+ 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()
+
+ }
+
+ initLiveTimestamps()
+}