GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#2835)
by
unknown
06:01
created

symphony/assets/js/src/symphony.collapsible.js   C

Complexity

Total Complexity 57
Complexity/F 1.84

Size

Lines of Code 304
Function Count 31

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
c 0
b 0
f 0
nc 1152
dl 0
loc 304
rs 5.7222
wmc 57
mnd 2
bc 49
fnc 31
bpm 1.5806
cpm 1.8387
noi 2

2 Functions

Rating   Name   Duplication   Size   Complexity  
B $.fn.symphonyCollapsible 0 264 1
A symphony.collapsible.js ➔ save 0 11 2

How to fix   Complexity   

Complexity

Complex classes like symphony/assets/js/src/symphony.collapsible.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
 * @package assets
3
 */
4
5
(function($, Symphony) {
6
	'use strict';
7
8
	// Saves the value into the local storage at the specified storage key.
9
	var save = function (storage, value) {
10
		// Put in a try/catch in case something goes wrong (no space, privileges etc)
11
		// Always put try/catches into their own function to prevent callers from
12
		// going into un-optimized hell
13
		try {
14
			window.localStorage[storage] = value;
15
		}
16
		catch(e) {
17
			window.onerror(e.message);
18
		}
19
	};
20
21
	/**
22
	 * Create collapsible elements.
23
	 *
24
	 * @name $.symphonyCollapsible
25
	 * @class
26
	 *
27
	 * @param {Object} options An object specifying containing the attributes specified below
28
	 * @param {String} [options.items='.instance'] Selector to find collapsible items within the container
29
	 * @param {String} [options.handles='.header:first'] Selector to find clickable handles to trigger interaction
30
	 * @param {String} [options.content='.content'] Selector to find hideable content area
31
	 * @param {Boolean} [options.save_state=true] Stores states of instances using local storage
32
	 * @param {String} [options.storage='symphony.collapsible.area.page.id'] Namespace used for local storage
33
	 * @param {Integer} [options.delay=250] Time delay for animations
34
	 *
35
	 * @example
36
37
			var collapsible = $('#duplicator').symphonyCollapsible({
38
				items:		'.instance',
39
				handles:	'.header span'
40
			});
41
			collapsible.collapseAll();
42
	 */
43
	$.fn.symphonyCollapsible = function(options) {
44
		var objects = this,
45
			settings = {
46
				items: '.instance',
47
				handles: '.frame-header',
48
				content: '.content',
49
				ignore: '.ignore',
50
				save_state: true,
51
				storage: 'symphony.collapsible.' + Symphony.Context.get('context-id'),
52
				delay: 250
53
			};
54
55
		$.extend(settings, options);
56
57
	/*-----------------------------------------------------------------------*/
58
59
		objects.each(function collapsible(index) {
60
			var object = $(this),
61
				storage = settings.storage + '.' + index + '.collapsed';
62
63
			var getDuration = function (duration) {
64
				return $.isNumeric(duration) ? duration : settings.delay;
65
			};
66
67
		/*---------------------------------------------------------------------
68
			Events
69
		---------------------------------------------------------------------*/
70
71
			var collapseItem = function collapse(item, duration) {
72
				var heightMin = 0;
73
74
				// Customization point
75
				item.trigger('collapsebefore.collapsible', settings);
76
77
				heightMin = item.data('heightMin');
78
79
				// Check duration
80
				if(duration !== 0) {
81
					item.addClass('js-animate');
82
					item.trigger('collapsestart.collapsible');
83
				}
84
85
				// Collapse item
86
				item.addClass('collapsed');
87
				item.css('max-height', heightMin);
88
89
				if(duration !== 0) {
90
					setTimeout(function() {
91
						item.trigger('animationend.collapsible');
92
						item.trigger('animationend.duplicator');
93
					}, duration);
94
				}
95
			};
96
97
			// Collapse item
98
			object.on('collapse.collapsible', settings.items, function collapse(event, duration) {
99
				var item = $(this);
100
				collapseItem(item, getDuration(duration));
101
			});
102
103
			// Collapse all items
104
			object.on('collapseall.collapsible', function collapseAll() {
105
				var items = object.find(settings.items + ':not(.collapsed)'),
106
					visibles = Symphony.Utilities.inSight(items),
107
					invisibles = $(),
108
					scrollTop = $(window).scrollTop(),
109
					visibleIndex = visibles.eq(0).index(),
110
					visibleCollapsedHeight = 0;
111
112
				// Find items that will be visible after collapse
113
				while (visibleIndex < items.length && visibleCollapsedHeight < window.innerHeight) {
114
					var currentItem = items.eq(visibleIndex);
115
					visibles = visibles.add(currentItem);
116
					visibleCollapsedHeight += currentItem.data('heightMin');
117
					visibleIndex++;
118
				}
119
				visibles.each(function () { collapseItem($(this), settings.delay); });
120
121
				setTimeout(function collapseAllInvisibleEnd() {
122
					var first = visibles.eq(0);
123
					var firstOffset = !first.length ? 0 : first.offset().top;
124
					// update invisible accordingly
125
					invisibles = items.not(visibles);
126
					invisibles.each(function () { collapseItem($(this), 0); });
127
					if (firstOffset > 0 && scrollTop > object.offset().top) {
128
						// scroll back to where we were,
129
						// which is last scroll position + delta of first visible item
130
						$(window).scrollTop(scrollTop + (first.offset().top - firstOffset));
131
					}
132
					invisibles.trigger('animationend.collapsible');
133
				}, settings.delay + 100);
134
			});
135
136
			// Expand item
137
			var expandItem = function (item, duration) {
138
				var heightMax = 0;
139
140
				// Customization point
141
				item.trigger('expandbefore.collapsible', settings);
142
143
				heightMax = item.data('heightMax');
144
145
				// Check duration
146
				if(duration !== 0) {
147
					item.addClass('js-animate');
148
					item.trigger('expandstart.collapsible');
149
				}
150
151
				// Collapse item
152
				item.removeClass('collapsed');
153
				item.css('max-height', heightMax);
154
155
				if(duration !== 0) {
156
					setTimeout(function() {
157
						item.trigger('animationend.collapsible');
158
					}, duration);
159
				}
160
			};
161
162
			object.on('expand.collapsible', settings.items, function expand(event, duration) {
163
				var item = $(this);
164
				expandItem(item, getDuration(duration));
165
			});
166
167
			// Expand all items
168
			object.on('expandall.collapsible', function expandAll() {
169
				var items = object.find(settings.items + '.collapsed'),
170
					visibles = Symphony.Utilities.inSight(items).filter('*:lt(4)'),
171
					invisibles = items.not(visibles),
172
					scrollTop = $(window).scrollTop();
173
174
				visibles.addClass('js-animate-all'); // prevent focus
175
				visibles.each(function () { expandItem($(this), settings.delay); });
176
				setTimeout(function expandAllInvisible() {
177
					var first = visibles.eq(0);
178
					var firstOffset = !first.length ? 0 : first.offset().top;
179
					invisibles.addClass('js-animate-all'); // prevent focus
180
					invisibles.each(function () { expandItem($(this), 0); });
181
					invisibles.trigger('animationend.collapsible');
182
					// if we are past the first item
183
					if (firstOffset > 0 && scrollTop > object.offset().top) {
184
						// scroll back to where we were,
185
						// which is last scroll position + delta of first visible item
186
						$(window).scrollTop(scrollTop + (first.offset().top - firstOffset));
187
					}
188
				}, settings.delay + 100);
189
			});
190
191
			// Finish animations
192
			object.on('animationend.collapsible', settings.items, function finish() {
193
				var item = $(this);
194
195
				// Trigger events
196
				if(item.is('.collapsed')) {
197
					item.trigger('collapsestop.collapsible');
198
				}
199
				else {
200
					item.trigger('expandstop.collapsible');
201
				}
202
203
				// clean up
204
				item.removeClass('js-animate js-animate-all');
205
			});
206
207
			// Toggle single item
208
			object.on('click.collapsible', settings.handles, function toggle(event) {
209
				var handle = $(this),
210
					item = handle.closest(settings.items);
211
212
				if(!handle.is(settings.ignore) && !$(event.target).is(settings.ignore) && !item.is('.locked')) {
213
214
					// Expand
215
					if(item.is('.collapsed')) {
216
						expandItem(item, settings.delay);
217
					}
218
219
					// Collapse
220
					else {
221
						collapseItem(item, settings.delay);
222
					}
223
				}
224
			});
225
226
			// Save states
227
			var saveTimer = 0;
228
			object.on('collapsestop.collapsible expandstop.collapsible store.collapsible', settings.items, function saveState() {
229
				if(settings.save_state === true && Symphony.Support.localStorage === true) {
230
					// save it to local storage, delayed, once
231
					clearTimeout(saveTimer);
232
					saveTimer = setTimeout(function () {
233
						var collapsed = object.find(settings.items).map(function(index) {
234
							if($(this).is('.collapsed')) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if $(this).is(".collapsed") 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...
235
								return index;
236
							}
237
						}).get().join(',');
238
239
						save(storage, collapsed);
240
					}, settings.delay);
241
				}
242
			});
243
244
			// Restore states
245
			object.on('restore.collapsible', function restoreState() {
246
				if(settings.save_state === true && Symphony.Support.localStorage === true && window.localStorage[storage]) {
247
					$.each(window.localStorage[storage].split(','), function(index, value) {
248
						var collapsed = object.find(settings.items).eq(value);
249
						if(collapsed.has('.invalid').length == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing collapsed.has(".invalid").length to 0 using the == operator is not safe. Consider using === instead.
Loading history...
250
							collapseItem(collapsed, 0);
251
						}
252
					});
253
				}
254
			});
255
256
			// Refresh state storage after ordering
257
			object.on('orderstop.orderable', function refreshOrderedState() {
258
				object.find(settings.items).trigger('store.collapsible');
259
			});
260
261
			// Refresh state storage after deleting and instance
262
			object.on('destructstop.duplicator', settings.items, function refreshState() {
263
				$(this).trigger('store.collapsible');
264
			});
265
266
			// Update sizes
267
			object.on('updatesize.collapsible', settings.items, function updateSizes() {
268
				var item = $(this),
269
					min = item.find(settings.handles).outerHeight(true),
270
					max = min + item.find(settings.content).outerHeight(true);
271
272
				item.data('heightMin', min);
273
				item.data('heightMax', max);
274
			});
275
276
			// Set sizes
277
			object.on('setsize.collapsible', settings.items, function setSizes() {
278
				var item = $(this);
279
				var heightMin = item.data('heightMin');
280
				var heightMax = item.data('heightMax');
281
				item.css({
282
					'min-height': heightMin,
283
					'max-height': heightMax
284
				});
285
			});
286
287
		/*---------------------------------------------------------------------
288
			Initialisation
289
		---------------------------------------------------------------------*/
290
291
			// Prepare interface
292
			object.addClass('collapsible').find(settings.items).each(function() {
293
				var item = $(this);
294
				item.addClass('instance');
295
				item.trigger('updatesize.collapsible');
296
				item.trigger('setsize.collapsible');
297
			});
298
299
			// Restore states
300
			object.trigger('restore.collapsible');
301
		});
302
303
	/*-----------------------------------------------------------------------*/
304
305
		return objects;
306
	};
307
308
})(window.jQuery, window.Symphony);
309