summaryrefslogtreecommitdiffstats
path: root/lib/SVG/Graph/Bar.rb
blob: 6951565945d2980bf3d743ecee5c06f48d5f9650 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
require 'rexml/document'
require 'SVG/Graph/Graph'
require 'SVG/Graph/BarBase'

module SVG
  module Graph
    # === Create presentation quality SVG bar graphs easily
    #
    # = Synopsis
    #
    #   require 'SVG/Graph/Bar'
    #
    #   fields = %w(Jan Feb Mar);
    #   data_sales_02 = [12, 45, 21]
    #
    #   graph = SVG::Graph::Bar.new(
    #     :height => 500,
    #     :width => 300,
    #     :fields => fields
    #   )
    #
    #   graph.add_data(
    #     :data => data_sales_02,
    #     :title => 'Sales 2002'
    #   )
    #
    #   print "Content-type: image/svg+xml\r\n\r\n"
    #   print graph.burn
    #
    # = Description
    #
    # This object aims to allow you to easily create high quality
    # SVG[http://www.w3c.org/tr/svg bar graphs. You can either use the default
    # style sheet or supply your own. Either way there are many options which
    # can be configured to give you control over how the graph is generated -
    # with or without a key, data elements at each point, title, subtitle etc.
    #
    # = Notes
    #
    # The default stylesheet handles upto 12 data sets, if you
    # use more you must create your own stylesheet and add the
    # additional settings for the extra data sets. You will know
    # if you go over 12 data sets as they will have no style and
    # be in black.
    #
    # = Examples
    #
    # * http://germane-software.com/repositories/public/SVG/test/test.rb
    #
    # = See also
    #
    # * SVG::Graph::Graph
    # * SVG::Graph::BarHorizontal
    # * SVG::Graph::Line
    # * SVG::Graph::Pie
    # * SVG::Graph::Plot
    # * SVG::Graph::TimeSeries
    class Bar < BarBase
      include REXML

      # See Graph::initialize and BarBase::set_defaults
      def set_defaults 
        super
        self.top_align = self.top_font = 1
      end

      protected

      def get_x_labels
        @config[:fields]
      end

      def get_y_labels
        maxvalue = max_value
        minvalue = min_value
        range = maxvalue - minvalue

        top_pad = range == 0 ? 10 : range / 20.0
        scale_range = (maxvalue + top_pad) - minvalue

        scale_division = scale_divisions || (scale_range / 10.0)

        if scale_integers
          scale_division = scale_division < 1 ? 1 : scale_division.round
        end

        rv = []
        maxvalue = maxvalue%scale_division == 0 ? 
          maxvalue : maxvalue + scale_division
        minvalue.step( maxvalue, scale_division ) {|v| rv << v}
        return rv
      end

      def x_label_offset( width )
        width / 2.0
      end

      def draw_data
        minvalue = min_value
        fieldwidth = field_width

        unit_size =  (@graph_height.to_f - font_size*2*top_font) / 
                          (get_y_labels.max - get_y_labels.min)
        bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0

        bar_width = fieldwidth - bargap
        bar_width /= @data.length if stack == :side
        x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
 
        bottom = @graph_height

        field_count = 0
        @config[:fields].each_index { |i|
          dataset_count = 0
          for dataset in @data
          
            # cases (assume 0 = +ve):
            #   value  min  length
            #    +ve   +ve  value - min
            #    +ve   -ve  value - 0
            #    -ve   -ve  value.abs - 0
          
            value = dataset[:data][i]
            
            left = (fieldwidth * field_count)
            
            length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
            # top is 0 if value is negative
            top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
            left += bar_width * dataset_count if stack == :side
 
            @graph.add_element( "rect", {
              "x" => left.to_s,
              "y" => top.to_s,
              "width" => bar_width.to_s,
              "height" => length.to_s,
              "class" => "fill#{dataset_count+1}"
            })

            make_datapoint_text(left + bar_width/2.0, top - 6, value.to_s)
            dataset_count += 1
          end
          field_count += 1
        }
      end
    end
  end
end
9'>backport/48345/stable29 Nextcloud server, a safe home for all your data: https://github.com/nextcloud/serverwww-data
aboutsummaryrefslogtreecommitdiffstats
path: root/core/src/session-heartbeat.js
blob: 3bd4d6b9ccdee68ff395580888deba7de2d8d627 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
 * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

import $ from 'jquery'
import { emit } from '@nextcloud/event-bus'
import { loadState } from '@nextcloud/initial-state'
import { getCurrentUser } from '@nextcloud/auth'
import { generateUrl } from '@nextcloud/router'

import OC from './OC/index.js'
import { setToken as setRequestToken, getToken as getRequestToken } from './OC/requesttoken.js'

let config = null
/**
 * The legacy jsunit tests overwrite OC.config before calling initCore
 * therefore we need to wait with assigning the config fallback until initCore calls initSessionHeartBeat
 */
const loadConfig = () => {
	try {
		config = loadState('core', 'config')
	} catch (e) {
		// This fallback is just for our legacy jsunit tests since we have no way to mock loadState calls
		config = OC.config
	}
}

/**
 * session heartbeat (defaults to enabled)
 *
 * @return {boolean}
 */
const keepSessionAlive = () => {
	return config.session_keepalive === undefined
		|| !!config.session_keepalive
}

/**
 * get interval in seconds
 *
 * @return {number}
 */
const getInterval = () => {
	let interval = NaN
	if (config.session_lifetime) {
		interval = Math.floor(config.session_lifetime / 2)
	}

	// minimum one minute, max 24 hours, default 15 minutes
	return Math.min(
		24 * 3600,
		Math.max(
			60,
			isNaN(interval) ? 900 : interval,
		),
	)
}

const getToken = async () => {
	const url = generateUrl('/csrftoken')

	// Not using Axios here as Axios is not stubbable with the sinon fake server
	// see https://stackoverflow.com/questions/41516044/sinon-mocha-test-with-async-ajax-calls-didnt-return-promises
	// see js/tests/specs/coreSpec.js for the tests
	const resp = await $.get(url)

	return resp.token
}

const poll = async () => {
	try {
		const token = await getToken()
		setRequestToken(token)
	} catch (e) {
		console.error('session heartbeat failed', e)
	}
}

const startPolling = () => {
	const interval = setInterval(poll, getInterval() * 1000)

	console.info('session heartbeat polling started')

	return interval
}

const registerAutoLogout = () => {
	if (!config.auto_logout || !getCurrentUser()) {
		return
	}

	let lastActive = Date.now()
	window.addEventListener('mousemove', e => {
		lastActive = Date.now()
		localStorage.setItem('lastActive', lastActive)
	})

	window.addEventListener('touchstart', e => {
		lastActive = Date.now()
		localStorage.setItem('lastActive', lastActive)
	})

	window.addEventListener('storage', e => {
		if (e.key !== 'lastActive') {
			return
		}
		lastActive = e.newValue
	})

	let intervalId = 0
	const logoutCheck = () => {
		const timeout = Date.now() - config.session_lifetime * 1000
		if (lastActive < timeout) {
			clearTimeout(intervalId)
			console.info('Inactivity timout reached, logging out')
			const logoutUrl = generateUrl('/logout') + '?requesttoken=' + encodeURIComponent(getRequestToken())
			window.location = logoutUrl
		}
	}
	intervalId = setInterval(logoutCheck, 1000)
}

/**
 * Calls the server periodically to ensure that session and CSRF
 * token doesn't expire
 */
export const initSessionHeartBeat = () => {
	loadConfig()

	registerAutoLogout()

	if (!keepSessionAlive()) {
		console.info('session heartbeat disabled')
		return
	}
	let interval = startPolling()

	window.addEventListener('online', async () => {
		console.info('browser is online again, resuming heartbeat')
		interval = startPolling()
		try {
			await poll()
			console.info('session token successfully updated after resuming network')

			// Let apps know we're online and requests will have the new token
			emit('networkOnline', {
				success: true,
			})
		} catch (e) {
			console.error('could not update session token after resuming network', e)

			// Let apps know we're online but requests might have an outdated token
			emit('networkOnline', {
				success: false,
			})
		}
	})
	window.addEventListener('offline', () => {
		console.info('browser is offline, stopping heartbeat')

		// Let apps know we're offline
		emit('networkOffline', {})

		clearInterval(interval)
		console.info('session heartbeat polling stopped')
	})
}