js/galleryutility.js   B
last analyzed

Complexity

Total Complexity 43
Complexity/F 2.39

Size

Lines of Code 303
Function Count 18

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 130
dl 0
loc 303
rs 8.96
c 0
b 0
f 0
wmc 43
mnd 25
bc 25
fnc 18
bpm 1.3888
cpm 2.3887
noi 6

How to fix   Complexity   

Complexity

Complex classes like js/galleryutility.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * Nextcloud - Gallery
3
 *
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 *
10
 * @copyright Olivier Paroz 2017
11
 */
12
/* global DOMPurify, oc_requesttoken, Gallery */
13
14
// The Utility class can also be loaded by the Files app
15
window.Gallery = window.Gallery || {};
16
17
(function ($, OC, t, oc_requesttoken, Gallery) {
18
	"use strict";
19
	/**
20
	 * Contains utility methods
21
	 *
22
	 * @fixme OC.generateUrl, OC.buildQueryString, OC.Notification are private APIs
23
	 *
24
	 * @constructor
25
	 */
26
	var Utility = function () {
27
	};
28
29
	Utility.prototype = {
30
		/**
31
		 * Detects if the browser is a recent or an old version of Internet Explorer
32
		 *
33
		 * @returns {string|boolean}
34
		 */
35
		getIeVersion: function () {
36
			// Blocking IE8
37
			if ($('html').is('.ie8')) {
38
				return 'unsupportedIe';
39
			} else if (navigator.userAgent.indexOf("MSIE") > 0) {
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
40
				return 'unsupportedIe';
41
			} else if (!!navigator.userAgent.match(/Trident.*rv[ :]*11\./)) {
42
				return 'modernIe';
43
			} else if (navigator.userAgent.indexOf("Edge/") > 0) {
44
				return 'edge';
45
			}
46
47
			return false;
48
		},
49
50
		/**
51
		 * Shows a notification to IE users, letting them know that they should use another browser
52
		 * in order to get the best experience
53
		 *
54
		 * @param {string} version
55
		 */
56
		showIeWarning: function (version) {
57
			var line1 = t('gallery', 'This application may not work properly on your browser.');
58
			var line2 = t('gallery',
59
				'For an improved experience, please install one of the following alternatives');
60
			var timeout = 15;
61
			if (version === 'unsupportedIe') {
62
				line1 = t('gallery', 'Your browser is not supported!');
63
				line2 = t('gallery', 'please install one of the following alternatives');
64
				timeout = 60;
65
			}
66
67
			var recommendedBrowsers = '</br>' +
68
				'<a href="http://www.getfirefox.com"><strong>Mozilla Firefox</strong></a> or ' +
69
				'<a href="https://www.google.com/chrome/"><strong>Google Chrome</strong></a>' +
70
				'</br>';
71
72
			var text = '<strong>' + line1 + '</strong></br>' + line2 + recommendedBrowsers;
73
			this.showHtmlNotification(text, timeout);
74
		},
75
76
		/**
77
		 * Shows a notification at the top of the screen
78
		 *
79
		 * @param {string} text
80
		 * @param {int} timeout
81
		 */
82
		showHtmlNotification: function (text, timeout) {
83
			var options = {
84
				timeout: timeout,
85
				isHTML: true
86
			};
87
			OC.Notification.showTemporary(text, options);
88
		},
89
90
		/**
91
		 * Returns the token allowing access to files shared via link
92
		 *
93
		 * @returns {string}
94
		 */
95
		getPublicToken: function () {
96
			var element = $('#gallery');
97
			var token;
98
99
			if (element.data('token')) {
100
				token = element.data('token');
101
			}
102
103
			if (element.data('requesttoken')) {
104
				/* jshint camelcase: false */
105
				oc_requesttoken = element.data('requesttoken');
0 ignored issues
show
Unused Code introduced by
The variable oc_requesttoken seems to be never used. Consider removing it.
Loading history...
106
			}
107
108
			return token;
0 ignored issues
show
Bug introduced by
The variable token does not seem to be initialized in case element.data("token") on line 99 is false. Are you sure this can never be the case?
Loading history...
109
		},
110
111
		/**
112
		 * Returns the host we can use for WebDAV
113
		 * 
114
		 * On public galleries, we need to provide the token as authorization
115
		 *
116
		 * @returns {string}
117
		 */
118
		getWebdavHost: function () {
119
			var host = OC.getHost();
120
			if (Gallery.token) {
121
				host = Gallery.token + '@' + host;
122
			}
123
124
			return host;
125
		},
126
127
		/**
128
		 * Returns the WebDAV endpoint we can use for files operations
129
		 *
130
		 * @returns {string}
131
		 */
132
		getWebdavRoot: function () {
133
			var root = OC.linkToRemoteBase('webdav');
134
			if (Gallery.token) {
135
				root = root.replace('remote.php', 'public.php');
136
			}
137
138
			return root;
139
		},
140
141
		/**
142
		 * Builds the URL which will retrieve a large preview of the file
143
		 *
144
		 * @fixme we cannot get rid of oc_requesttoken parameter as it's missing from the headers
145
		 *
146
		 * @param {number} fileId
147
		 * @param {number} etag
148
		 *
149
		 * @return {string}
150
		 */
151
		getPreviewUrl: function (fileId, etag) {
0 ignored issues
show
Unused Code introduced by
The parameter etag is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
152
			var width = Math.ceil(screen.width * window.devicePixelRatio);
0 ignored issues
show
Bug introduced by
The variable screen seems to be never declared. If this is a global, consider adding a /** global: screen */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
153
			var height = Math.ceil(screen.height * window.devicePixelRatio);
154
155
			/* Find value of longest edge. */
156
			var longEdge = Math.max(width, height);
157
158
			/* Find the next larger image size. */
159
			if (longEdge % 100 !== 0) {
160
				longEdge = ( longEdge + 100 ) - ( longEdge % 100 );
161
			}
162
163
			/* jshint camelcase: false */
164
			var params = {
165
				width: longEdge,
166
				height: longEdge,
167
			};
168
			return this.buildGalleryUrl('preview', '/' + fileId, params);
169
		},
170
171
		/**
172
		 * Builds a URL pointing to one of the app's controllers
173
		 *
174
		 * @param {string} endPoint
175
		 * @param {undefined|string} path
176
		 * @param params
177
		 *
178
		 * @returns {string}
179
		 */
180
		buildGalleryUrl: function (endPoint, path, params) {
181
			if (path === undefined) {
182
				path = '';
183
			}
184
			var extension = '';
185
			if (Gallery.token) {
186
				params.token = Gallery.token;
187
				extension = '.public';
188
			}
189
			var query = OC.buildQueryString(params);
190
			return OC.generateUrl('apps/' + Gallery.appName + '/' + endPoint + extension + path,
191
					null) +
192
				'?' +
193
				query;
194
		},
195
196
		/**
197
		 * Builds a URL pointing to one of the files' controllers
198
		 *
199
		 * @param {string} path
200
		 * @param {string} files
201
		 *
202
		 * @returns {string}
203
		 */
204
		buildFilesUrl: function (path, files) {
205
			var subUrl = '';
206
			var params = {
207
				path: path,
208
				files: files
209
			};
210
211
			if (Gallery.token) {
212
				params.token = Gallery.token;
213
				subUrl = 's/{token}/download?dir={path}&files={files}';
214
			} else {
215
				subUrl = 'apps/files/ajax/download.php?dir={path}&files={files}';
216
			}
217
218
			return OC.generateUrl(subUrl, params);
219
		},
220
221
		/**
222
		 * Sorts arrays based on name or date
223
		 *
224
		 * @param {string} sortType
225
		 * @param {string} sortOrder
226
		 *
227
		 * @returns {Function}
228
		 */
229
		sortBy: function (sortType, sortOrder) {
230
			if (sortType === 'name') {
231
				if (sortOrder === 'asc') {
232
					//sortByNameAsc
233
					return function (a, b) {
234
						return OC.Util.naturalSortCompare(a.path, b.path);
235
					};
236
				}
237
				//sortByNameDes
238
				return function (a, b) {
239
					return -OC.Util.naturalSortCompare(a.path, b.path);
240
				};
241
			}
242
			if (sortType === 'date') {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if sortType === "date" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
243
				if (sortOrder === 'asc') {
244
					//sortByDateAsc
245
					return function (a, b) {
246
						return b.mTime - a.mTime;
247
					};
248
				}
249
				//sortByDateDes
250
				return function (a, b) {
251
					return a.mTime - b.mTime;
252
				};
253
			}
254
		},
255
256
		/**
257
		 * Adds hooks to DOMPurify
258
		 */
259
		addDomPurifyHooks: function () {
260
			// allowed URI schemes
261
			var whitelist = ['http', 'https'];
262
263
			// build fitting regex
264
			var regex = new RegExp('^(' + whitelist.join('|') + '):', 'gim');
265
266
			DOMPurify.addHook('afterSanitizeAttributes', function (node) {
267
				// This hook enforces URI scheme whitelist
268
				// @link
269
				// https://github.com/cure53/DOMPurify/blob/master/demos/hooks-scheme-whitelist.html
270
271
				// build an anchor to map URLs to
272
				var anchor = document.createElement('a');
273
274
				// check all href attributes for validity
275
				if (node.hasAttribute('href')) {
276
					anchor.href = node.getAttribute('href');
277
					if (anchor.protocol && !anchor.protocol.match(regex)) {
278
						node.removeAttribute('href');
279
					}
280
				}
281
				// check all action attributes for validity
282
				if (node.hasAttribute('action')) {
283
					anchor.href = node.getAttribute('action');
284
					if (anchor.protocol && !anchor.protocol.match(regex)) {
285
						node.removeAttribute('action');
286
					}
287
				}
288
				// check all xlink:href attributes for validity
289
				if (node.hasAttribute('xlink:href')) {
290
					anchor.href = node.getAttribute('xlink:href');
291
					if (anchor.protocol && !anchor.protocol.match(regex)) {
292
						node.removeAttribute('xlink:href');
293
					}
294
				}
295
296
				// This hook restores the proper, standard namespace in SVG files
297
				var encodedXmlns, decodedXmlns;
298
299
				// Restores namespaces which were put in the DOCTYPE by Illustrator
300
				if (node.hasAttribute('xmlns') && node.getAttribute('xmlns') === '&ns_svg;') {
301
					encodedXmlns = node.getAttribute('xmlns');
302
					decodedXmlns = encodedXmlns.replace('&ns_svg;', 'http://www.w3.org/2000/svg');
303
					node.setAttribute('xmlns', decodedXmlns);
304
				}
305
				if (node.hasAttribute('xmlns:xlink') &&
306
					node.getAttribute('xmlns:xlink') === '&ns_xlink;') {
307
					encodedXmlns = node.getAttribute('xmlns:xlink');
308
					decodedXmlns =
309
						encodedXmlns.replace('&ns_xlink;', 'http://www.w3.org/1999/xlink');
310
					node.setAttribute('xmlns:xlink', decodedXmlns);
311
				}
312
			});
313
		}
314
	};
315
316
	Gallery.Utility = Utility;
317
})(jQuery, OC, t, oc_requesttoken, Gallery);
318