js/slideshowcontrols.js   F
last analyzed

Complexity

Total Complexity 66
Complexity/F 1.78

Size

Lines of Code 495
Function Count 37

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 227
dl 0
loc 495
rs 3.12
c 0
b 0
f 0
wmc 66
mnd 29
bc 29
fnc 37
bpm 0.7836
cpm 1.7837
noi 5

How to fix   Complexity   

Complexity

Complex classes like js/slideshowcontrols.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 OC, SlideShow */
13
(function ($, SlideShow) {
14
	"use strict";
15
	/**
16
	 * Button and key controls for the slideshow
17
	 *
18
	 * @param {Object} slideshow
19
	 * @param {*} container
20
	 * @param {Object} zoomablePreview
21
	 * @param {number} interval
22
	 * @param {Array} features
23
	 * @param {Function} restoreContentCallback
24
	 * @constructor
25
	 */
26
	var Controls = function (slideshow, container, zoomablePreview, interval, features, restoreContentCallback) {
27
		this.slideshow = slideshow;
28
		this.container = container;
29
		this.zoomablePreview = zoomablePreview;
30
		this.progressBar = container.find('.progress');
31
		this.interval = interval || 5000;
32
		if (features.indexOf('background_colour_toggle') > -1) {
33
			this.backgroundToggle = true;
34
		}
35
		this.restoreContentCallback = restoreContentCallback;
36
	};
37
38
	Controls.prototype = {
39
		current: 0,
40
		errorLoadingImage: false,
41
		playTimeout: 0,
42
		playing: false,
43
		active: false,
44
		backgroundToggle: false,
45
46
		/**
47
		 * Initialises the controls
48
		 */
49
		init: function () {
50
			var makeCallBack = function (handler) {
51
				return function (evt) {
52
					if (!this.active) {
53
						return;
54
					}
55
					evt.stopPropagation();
56
					evt.preventDefault();
57
					handler.call(this);
58
				}.bind(this);
59
			}.bind(this);
60
61
			this._buttonSetup(makeCallBack);
62
			this._specialButtonSetup(makeCallBack);
63
			this._keyCodeSetup(makeCallBack);
64
		},
65
66
		/**
67
		 * Updates the controls
68
		 *
69
		 * @param {{name:string, url: string, path: string, fallBack: string}[]} images
70
		 * @param {boolean} autoPlay
71
		 */
72
		update: function (images, autoPlay) {
73
			this.images = images;
74
			this.active = true;
75
			this.showButton('.play');
76
			this.hideButton('.pause, .progress');
77
			this.playing = false;
78
79
			// Hide prev/next and play buttons when we only have one pic
80
			this.container.find('.next, .previous, .play').toggle(this.images.length > 1);
81
82
			// Hide the action buttons until we have something to show
83
			this.hideActionButtons();
84
85
			if (autoPlay) {
86
				this._playPauseToggle();
87
			}
88
		},
89
90
		/**
91
		 * Initialises local variables when the show starts
92
		 *
93
		 * @param {number} currentImageId
94
		 */
95
		show: function (currentImageId) {
96
			var currentImage = this.images[currentImageId];
97
			this.current = currentImageId;
98
			this.errorLoadingImage = false;
99
			if (this.playing) {
100
				this._setTimeout();
101
			}
102
			this._setName(currentImage.name);
103
		},
104
105
		/**
106
		 * Stops and hides the slideshow
107
		 */
108
		stop: function () {
109
			this._setName('');
110
			this.playing = false;
111
			this.slideshow.stop();
112
			this.zoomablePreview.stop();
113
114
			this._clearTimeout();
115
			this.restoreContentCallback();
116
			this.container.hide();
117
			this.active = false;
118
		},
119
120
		/**
121
		 * Updates the private variables in case of problems loading an image
122
		 *
123
		 * @param {Array} images
124
		 * @param {boolean} errorLoadingImage
125
		 */
126
		updateControls: function (images, errorLoadingImage) {
127
			this.images = images;
128
			this.errorLoadingImage = errorLoadingImage;
129
		},
130
131
		/**
132
		 * Shows the action buttons
133
		 *
134
		 * @param {boolean} transparent
135
		 * @param {boolean} isPublic
136
		 * @param {number} permissions
137
		 */
138
		showActionButtons: function (transparent, isPublic, permissions) {
139
			if (transparent) {
140
				this._showBackgroundToggle();
141
			}
142
			var hideDownload = $('#hideDownload').val();
143
			if (hideDownload !== 'true') {
144
				this.showButton('.downloadImage');
145
			}
146
			var canDelete = ((permissions & OC.PERMISSION_DELETE) !== 0);
147
			var canShare = ((permissions & OC.PERMISSION_SHARE) !== 0);
148
			if (!isPublic && canDelete) {
149
				this.showButton('.deleteImage');
150
			}
151
			if (!isPublic && canShare) {
152
				this.showButton('.shareImage');
153
			}
154
		},
155
156
		/**
157
		 * Hides the action buttons
158
		 */
159
		hideActionButtons: function () {
160
			this.hideButton('.changeBackground');
161
			this.hideButton('.downloadImage');
162
			this.hideButton('.deleteImage');
163
			this.hideButton('.shareImage');
164
		},
165
166
		/**
167
		 * Shows a button which has been hidden
168
		 */
169
		showButton: function (button) {
170
			this.container.find(button).removeClass('hidden');
171
		},
172
173
		/**
174
		 * Hides a button
175
		 *
176
		 * @param button
177
		 */
178
		hideButton: function (button) {
179
			this.container.find(button).addClass('hidden');
180
		},
181
182
		/**
183
		 * Removes a button
184
		 *
185
		 * @param button
186
		 */
187
		removeButton: function (button) {
188
			this.container.find(button).remove();
189
		},
190
191
		/**
192
		 * Sets up the button based navigation
193
		 *
194
		 * @param {Function} makeCallBack
195
		 * @private
196
		 */
197
		_buttonSetup: function (makeCallBack) {
198
			this.container.children('.next').click(makeCallBack(this._next));
199
			this.container.children('.previous').click(makeCallBack(this._previous));
200
			this.container.children('.exit').click(makeCallBack(this._exit));
201
			this.container.children('.pause, .play').click(makeCallBack(this._playPauseToggle));
202
			this.progressBar.click(makeCallBack(this._playPauseToggle));
203
			this.container.children('.previous, .next, .slideshow-menu, .name').on(
204
				'mousewheel DOMMouseScroll mousemove', function (evn) {
205
					this.container.children('.bigshotContainer')[0].dispatchEvent(
206
						new WheelEvent(evn.originalEvent.type, evn.originalEvent));
0 ignored issues
show
Bug introduced by
The variable WheelEvent seems to be never declared. If this is a global, consider adding a /** global: WheelEvent */ 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...
207
				}.bind(this));
208
		},
209
210
		/**
211
		 * Sets up additional buttons
212
		 *
213
		 * @param {Function} makeCallBack
214
		 * @private
215
		 */
216
		_specialButtonSetup: function (makeCallBack) {
217
			this.container.find('.downloadImage').click(makeCallBack(this._getImageDownload));
218
			this.container.find('.deleteImage').click(makeCallBack(this._deleteImage));
219
			this.container.find('.shareImage').click(makeCallBack(this.share));
220
			this.container.find('.slideshow-menu').width = 52;
221
			if (this.backgroundToggle) {
222
				this.container.find('.changeBackground').click(
223
					makeCallBack(this._toggleBackground));
224
				this.container.find('.slideshow-menu').width += 52;
225
			} else {
226
				this.hideButton('.changeBackground');
227
			}
228
		},
229
230
		/**
231
		 * Populates the share dialog with the needed information
232
		 */
233
		share: function () {
234
			var image = this.images[this.current];
235
			if (!Gallery.Share.droppedDown) {
0 ignored issues
show
Bug introduced by
The variable Gallery seems to be never declared. If this is a global, consider adding a /** global: Gallery */ 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...
236
237
				$('.slideshow-menu a.share').data('path', image.path)
238
					.data('link', true)
239
					.data('item-source', image.file)
240
					.data('possible-permissions', image.permissions)
241
					.click();
242
			}
243
		},
244
245
		/**
246
		 * Shows the background colour switcher, if activated in the configuration
247
		 */
248
		_showBackgroundToggle: function () {
249
			if (this.backgroundToggle) {
250
				this.showButton('.changeBackground');
251
			}
252
		},
253
254
		/**
255
		 * Sets up the key based controls
256
		 *
257
		 * @param {Function} makeCallBack
258
		 * @private
259
		 */
260
		_keyCodeSetup: function (makeCallBack) {
261
			$(document).keyup(function (evt) {
262
				if (evt.target.tagName.toLowerCase() === 'input') {
263
					return;
264
				}
265
266
				var escKey = 27;
267
				var leftKey = 37;
268
				var rightKey = 39;
269
				var spaceKey = 32;
270
				var fKey = 70;
271
				var zoomOutKeys = [48, 96, 79, 40]; // zeros, o or down key
272
				var zoomInKeys = [57, 105, 73, 38]; // 9, i or up key
273
				if (evt.keyCode === escKey) {
274
					makeCallBack(this._exit)(evt);
275
				} else if (evt.keyCode === leftKey) {
276
					makeCallBack(this._previous)(evt);
277
				} else if (evt.keyCode === rightKey) {
278
					makeCallBack(this._next)(evt);
279
				} else if (evt.keyCode === spaceKey) {
280
					makeCallBack(this._playPauseToggle)(evt);
281
				} else if (evt.keyCode === fKey) {
282
					makeCallBack(this._fullScreenToggle)(evt);
283
				} else if (this._hasKeyBeenPressed(evt, zoomOutKeys)) {
284
					makeCallBack(this._zoomToOriginal)(evt);
285
				} else if (this._hasKeyBeenPressed(evt, zoomInKeys)) {
286
					makeCallBack(this._zoomToFit)(evt);
287
				}
288
			}.bind(this));
289
		},
290
291
		/**
292
		 * Determines if a key has been pressed by comparing the event and the key
293
		 *
294
		 * @param evt
295
		 * @param {Array} keys
296
		 *
297
		 * @returns {boolean}
298
		 * @private
299
		 */
300
		_hasKeyBeenPressed: function (evt, keys) {
301
			var i, keysLength = keys.length;
302
			for (i = 0; i < keysLength; i++) {
303
				if (evt.keyCode === keys[i]) {
304
					return true;
305
				}
306
			}
307
			return false;
308
		},
309
310
		/**
311
		 * Starts the slideshow timer
312
		 *
313
		 * @private
314
		 */
315
		_setTimeout: function () {
316
			this._clearTimeout();
317
			this.playTimeout = setTimeout(this._next.bind(this), this.interval);
318
			this.progressBar.stop();
319
			this.progressBar.css('height', '6px');
320
			this.progressBar.animate({'height': '26px'}, this.interval, 'linear');
321
		},
322
323
		/**
324
		 * Stops the slideshow timer
325
		 *
326
		 * @private
327
		 */
328
		_clearTimeout: function () {
329
			if (this.playTimeout) {
330
				clearTimeout(this.playTimeout);
331
			}
332
			this.progressBar.stop();
333
			this.progressBar.css('height', '6px');
334
			this.playTimeout = 0;
335
		},
336
337
		/**
338
		 * Starts/stops autoplay and shows/hides the play/pause buttons
339
		 *
340
		 * @private
341
		 */
342
		_playPauseToggle: function () {
343
			if (this.playing === true) {
344
				this.playing = false;
345
				this._clearTimeout();
346
			} else {
347
				this.playing = true;
348
				this._setTimeout();
349
			}
350
351
			this.container.find('.play, .pause, .progress').toggleClass('hidden');
352
		},
353
354
		/**
355
		 * Shows the next slide
356
		 *
357
		 * @private
358
		 */
359
		_next: function () {
360
			if(Gallery.Share){
0 ignored issues
show
Bug introduced by
The variable Gallery seems to be never declared. If this is a global, consider adding a /** global: Gallery */ 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...
361
				Gallery.Share.hideDropDown();
362
			}
363
			this._setName('');
364
			this.slideshow.hideErrorNotification();
365
			this.zoomablePreview.reset();
366
367
			if (this.errorLoadingImage) {
368
				this.current -= 1;
369
			}
370
			this.current = (this.current + 1) % this.images.length;
371
			var next = (this.current + 1) % this.images.length;
372
			this._updateSlideshow(next);
373
		},
374
375
		/**
376
		 * Shows the previous slide
377
		 *
378
		 * @private
379
		 */
380
		_previous: function () {
381
			if(Gallery.Share){
0 ignored issues
show
Bug introduced by
The variable Gallery seems to be never declared. If this is a global, consider adding a /** global: Gallery */ 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...
382
				Gallery.Share.hideDropDown();
383
			}
384
			this._setName('');
385
			this.slideshow.hideErrorNotification();
386
			this.zoomablePreview.reset();
387
388
			this.current = (this.current - 1 + this.images.length) % this.images.length;
389
			var previous = (this.current - 1 + this.images.length) % this.images.length;
390
			this._updateSlideshow(previous);
391
		},
392
393
		/**
394
		 * Asks the slideshow for the next image
395
		 *
396
		 * @param {number} imageId
397
		 * @private
398
		 */
399
		_updateSlideshow: function (imageId) {
400
			this.slideshow.next(this.current, this.images[imageId]);
401
		},
402
403
		/**
404
		 * Exits the slideshow by going back in history
405
		 *
406
		 * @private
407
		 */
408
		_exit: function () {
409
			if (Gallery.Share){
0 ignored issues
show
Bug introduced by
The variable Gallery seems to be never declared. If this is a global, consider adding a /** global: Gallery */ 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...
410
				Gallery.Share.hideDropDown();
411
			}
412
413
			// Only modern browsers can manipulate history
414
			if (history && history.replaceState) {
415
				// We simulate a click on the back button in order to be consistent
416
				window.history.back();
417
			} else {
418
				// For ancient browsers supported in core
419
				this.stop();
420
			}
421
		},
422
423
		/**
424
		 * Launches fullscreen mode if the browser supports it
425
		 *
426
		 * @private
427
		 */
428
		_fullScreenToggle: function () {
429
			this.zoomablePreview.fullScreenToggle();
430
		},
431
432
		/**
433
		 * Resizes the image to its original size
434
		 *
435
		 * @private
436
		 */
437
		_zoomToOriginal: function () {
438
			this.zoomablePreview.zoomToOriginal();
439
		},
440
441
		/**
442
		 * Fits the image in the browser window
443
		 *
444
		 * @private
445
		 */
446
		_zoomToFit: function () {
447
			this.zoomablePreview.zoomToFit();
448
		},
449
450
		/**
451
		 * Sends the current image as a download
452
		 *
453
		 * @returns {boolean}
454
		 * @private
455
		 */
456
		_getImageDownload: function () {
457
			var downloadUrl = this.images[this.current].downloadUrl;
458
459
			return this.slideshow.getImageDownload(downloadUrl);
460
		},
461
462
		/**
463
		 * Changes the colour of the background of the image
464
		 *
465
		 * @private
466
		 */
467
		_toggleBackground: function () {
468
			this.slideshow.toggleBackground();
469
		},
470
471
		/**
472
		 * Shows the filename of the current image
473
		 * @param {string} imageName
474
		 * @private
475
		 */
476
		_setName: function (imageName) {
477
			var nameElement = this.container.find('.title');
478
			nameElement.text(imageName);
479
		},
480
481
		/**
482
		 * Delete the image from the slideshow
483
		 * @private
484
		 */
485
		_deleteImage: function () {
486
			var image = this.images[this.current];
487
			var self = this;
488
			$.ajax({
489
				type: 'DELETE',
490
				url: OC.getRootPath() + '/remote.php/webdav/' + image.path,
491
				success: function () {
492
					self.slideshow.deleteImage(image, self.current);
493
					self.images.splice(self.current, 1);
494
					if (self.images.length === 0) {
495
						self._exit();
496
					}
497
					else {
498
						self.current = self.current % self.images.length;
499
						self._updateSlideshow(self.current);
500
					}
501
				}
502
			});
503
		}
504
	};
505
506
	SlideShow.Controls = Controls;
507
})(jQuery, SlideShow);
508