resources/lib/bootstrap-filestyle/bootstrap-filestyle.js   F
last analyzed

Complexity

Total Complexity 81
Complexity/F 3.24

Size

Lines of Code 342
Function Count 25

Duplication

Duplicated Lines 342
Ratio 100 %

Importance

Changes 0
Metric Value
cc 0
nc 65536
dl 342
loc 342
rs 3.12
c 0
b 0
f 0
wmc 81
mnd 4
bc 84
fnc 25
bpm 3.36
cpm 3.24
noi 14

19 Functions

Rating   Name   Duplication   Size   Complexity  
A Filestyle.destroy 4 4 1
B Filestyle.disabled 17 17 5
A $.fn.filestyle.noConflict 4 4 1
A Filestyle.buttonText 8 8 2
A Filestyle.size 14 14 3
A Filestyle.clear 5 5 1
A $.fn.filestyle 22 22 2
A Filestyle.htmlInput 7 7 3
B Filestyle.icon 15 15 5
B Filestyle.pushNameFiles 22 22 4
D Filestyle.constructor 70 70 9
A Filestyle.iconName 9 9 2
A Filestyle.buttonName 10 10 2
C Filestyle.input 31 31 8
A bootstrap-filestyle.js ➔ Filestyle 5 5 1
A bootstrap-filestyle.js ➔ $ 19 19 1
C Filestyle.buttonBefore 23 23 7
A Filestyle.htmlIcon 7 7 2
A Filestyle.placeholder 8 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like resources/lib/bootstrap-filestyle/bootstrap-filestyle.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
 * bootstrap-filestyle
3
 * doc: http://markusslima.github.io/bootstrap-filestyle/
4
 * github: https://github.com/markusslima/bootstrap-filestyle
5
 *
6
 * Copyright (c) 2014 Markus Vinicius da Silva Lima
7
 * Version 1.2.1
8
 * Licensed under the MIT license.
9
 */
10 View Code Duplication
(function($) {"use strict";
11
12
    var nextId = 0;
13
14
	var Filestyle = function(element, options) {
15
		this.options = options;
16
		this.$elementFilestyle = [];
17
		this.$element = $(element);
18
	};
19
20
	Filestyle.prototype = {
21
		clear : function() {
22
			this.$element.val('');
23
			this.$elementFilestyle.find(':text').val('');
24
			this.$elementFilestyle.find('.badge').remove();
25
		},
26
27
		destroy : function() {
28
			this.$element.removeAttr('style').removeData('filestyle');
29
			this.$elementFilestyle.remove();
30
		},
31
32
		disabled : function(value) {
33
			if (value === true) {
34
				if (!this.options.disabled) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !this.options.disabled 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...
35
					this.$element.attr('disabled', 'true');
36
					this.$elementFilestyle.find('label').attr('disabled', 'true');
37
					this.options.disabled = true;
38
				}
39
			} else if (value === false) {
40
				if (this.options.disabled) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.options.disabled 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...
41
					this.$element.removeAttr('disabled');
42
					this.$elementFilestyle.find('label').removeAttr('disabled');
43
					this.options.disabled = false;
44
				}
45
			} else {
46
				return this.options.disabled;
47
			}
48
		},
49
50
		buttonBefore : function(value) {
51
			if (value === true) {
52
				if (!this.options.buttonBefore) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !this.options.buttonBefore 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...
53
					this.options.buttonBefore = true;
54
					if (this.options.input) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.options.input 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...
55
						this.$elementFilestyle.remove();
56
						this.constructor();
57
						this.pushNameFiles();
58
					}
59
				}
60
			} else if (value === false) {
61
				if (this.options.buttonBefore) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.options.buttonBefore 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...
62
					this.options.buttonBefore = false;
63
					if (this.options.input) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.options.input 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...
64
						this.$elementFilestyle.remove();
65
						this.constructor();
66
						this.pushNameFiles();
67
					}
68
				}
69
			} else {
70
				return this.options.buttonBefore;
71
			}
72
		},
73
74
		icon : function(value) {
75
			if (value === true) {
76
				if (!this.options.icon) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !this.options.icon 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...
77
					this.options.icon = true;
78
					this.$elementFilestyle.find('label').prepend(this.htmlIcon());
79
				}
80
			} else if (value === false) {
81
				if (this.options.icon) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.options.icon 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...
82
					this.options.icon = false;
83
					this.$elementFilestyle.find('.icon-span-filestyle').remove();
84
				}
85
			} else {
86
				return this.options.icon;
87
			}
88
		},
89
		
90
		input : function(value) {
91
			if (value === true) {
92
				if (!this.options.input) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !this.options.input 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...
93
					this.options.input = true;
94
95
					if (this.options.buttonBefore) {
96
						this.$elementFilestyle.append(this.htmlInput());
97
					} else {
98
						this.$elementFilestyle.prepend(this.htmlInput());
99
					}
100
101
					this.$elementFilestyle.find('.badge').remove();
102
103
					this.pushNameFiles();
104
105
					this.$elementFilestyle.find('.group-span-filestyle').addClass('input-group-btn');
106
				}
107
			} else if (value === false) {
108
				if (this.options.input) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.options.input 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...
109
					this.options.input = false;
110
					this.$elementFilestyle.find(':text').remove();
111
					var files = this.pushNameFiles();
112
					if (files.length > 0 && this.options.badge) {
113
						this.$elementFilestyle.find('label').append(' <span class="badge">' + files.length + '</span>');
114
					}
115
					this.$elementFilestyle.find('.group-span-filestyle').removeClass('input-group-btn');
116
				}
117
			} else {
118
				return this.options.input;
119
			}
120
		},
121
122
		size : function(value) {
123
			if (value !== undefined) {
124
				var btn = this.$elementFilestyle.find('label'), input = this.$elementFilestyle.find('input');
125
126
				btn.removeClass('btn-lg btn-sm');
127
				input.removeClass('input-lg input-sm');
128
				if (value != 'nr') {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if value != "nr" 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...
129
					btn.addClass('btn-' + value);
130
					input.addClass('input-' + value);
131
				}
132
			} else {
133
				return this.options.size;
134
			}
135
		},
136
		
137
		placeholder : function(value) {
138
			if (value !== undefined) {
139
				this.options.placeholder = value;
140
				this.$elementFilestyle.find('input').attr('placeholder', value);
141
			} else {
142
				return this.options.placeholder;
143
			}
144
		},		
145
146
		buttonText : function(value) {
147
			if (value !== undefined) {
148
				this.options.buttonText = value;
149
				this.$elementFilestyle.find('label .buttonText').html(this.options.buttonText);
150
			} else {
151
				return this.options.buttonText;
152
			}
153
		},
154
		
155
		buttonName : function(value) {
156
			if (value !== undefined) {
157
				this.options.buttonName = value;
158
				this.$elementFilestyle.find('label').attr({
159
					'class' : 'btn ' + this.options.buttonName
160
				});
161
			} else {
162
				return this.options.buttonName;
163
			}
164
		},
165
166
		iconName : function(value) {
167
			if (value !== undefined) {
168
				this.$elementFilestyle.find('.icon-span-filestyle').attr({
169
					'class' : 'icon-span-filestyle ' + this.options.iconName
170
				});
171
			} else {
172
				return this.options.iconName;
173
			}
174
		},
175
176
		htmlIcon : function() {
177
			if (this.options.icon) {
178
				return '<span class="icon-span-filestyle ' + this.options.iconName + '"></span> ';
179
			} else {
180
				return '';
181
			}
182
		},
183
184
		htmlInput : function() {
185
			if (this.options.input) {
186
				return '<input type="text" class="form-control ' + (this.options.size == 'nr' ? '' : 'input-' + this.options.size) + '" placeholder="'+ this.options.placeholder +'" disabled> ';
187
			} else {
188
				return '';
189
			}
190
		},
191
192
		// puts the name of the input files
193
		// return files
194
		pushNameFiles : function() {
195
			var content = '', files = [];
196
			if (this.$element[0].files === undefined) {
197
				files[0] = {
198
					'name' : this.$element[0] && this.$element[0].value
199
				};
200
			} else {
201
				files = this.$element[0].files;
202
			}
203
204
			for (var i = 0; i < files.length; i++) {
205
				content += files[i].name.split("\\").pop() + ', ';
206
			}
207
208
			if (content !== '') {
209
				this.$elementFilestyle.find(':text').val(content.replace(/\, $/g, ''));
210
			} else {
211
				this.$elementFilestyle.find(':text').val('');
212
			}
213
			
214
			return files;
215
		},
216
217
		constructor : function() {
218
			var _self = this, 
219
				html = '', 
220
				id = _self.$element.attr('id'), 
221
				files = [], 
0 ignored issues
show
Unused Code introduced by
The variable files seems to be never used. Consider removing it.
Loading history...
222
				btn = '', 
223
				$label;
0 ignored issues
show
Unused Code introduced by
The variable $label seems to be never used. Consider removing it.
Loading history...
224
225
			if (id === '' || !id) {
226
				id = 'filestyle-' + nextId;
227
				_self.$element.attr({
228
					'id' : id
229
				});
230
                nextId++;
231
			}
232
233
			btn = '<span class="group-span-filestyle ' + (_self.options.input ? 'input-group-btn' : '') + '">' + 
234
			  '<label for="' + id + '" class="btn ' + _self.options.buttonName + ' ' + 
235
			(_self.options.size == 'nr' ? '' : 'btn-' + _self.options.size) + '" ' + 
236
			(_self.options.disabled ? 'disabled="true"' : '') + '>' + 
237
			_self.htmlIcon() + '<span class="buttonText">' + _self.options.buttonText + '</span>' + 
238
			  '</label>' + 
239
			  '</span>';
240
			
241
			html = _self.options.buttonBefore ? btn + _self.htmlInput() : _self.htmlInput() + btn;
242
			
243
			_self.$elementFilestyle = $('<div class="bootstrap-filestyle input-group">' + html + '</div>');
244
			_self.$elementFilestyle.find('.group-span-filestyle').attr('tabindex', "0").keypress(function(e) {
245
			if (e.keyCode === 13 || e.charCode === 32) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if e.keyCode === 13 || e.charCode === 32 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...
246
				_self.$elementFilestyle.find('label').click();
247
					return false;
248
				}
249
			});
250
251
			// hidding input file and add filestyle
252
			_self.$element.css({
253
				'position' : 'absolute',
254
				'clip' : 'rect(0px 0px 0px 0px)' // using 0px for work in IE8
255
			}).attr('tabindex', "-1").after(_self.$elementFilestyle);
256
257
			if (_self.options.disabled) {
258
				_self.$element.attr('disabled', 'true');
259
			}
260
261
			// Getting input file value
262
			_self.$element.change(function() {
263
				var files = _self.pushNameFiles();
264
265
				if (_self.options.input == false && _self.options.badge) {
266
					if (_self.$elementFilestyle.find('.badge').length == 0) {
267
						_self.$elementFilestyle.find('label').append(' <span class="badge">' + files.length + '</span>');
268
					} else if (files.length == 0) {
269
						_self.$elementFilestyle.find('.badge').remove();
270
					} else {
271
						_self.$elementFilestyle.find('.badge').html(files.length);
272
					}
273
				} else {
274
					_self.$elementFilestyle.find('.badge').remove();
275
				}
276
			});
277
278
			// Check if browser is Firefox
279
			if (window.navigator.userAgent.search(/firefox/i) > -1) {
280
				// Simulating choose file for firefox
281
				_self.$elementFilestyle.find('label').click(function() {
282
					_self.$element.click();
283
					return false;
284
				});
285
			}
286
		}
287
	};
288
289
	var old = $.fn.filestyle;
290
291
	$.fn.filestyle = function(option, value) {
292
		var get = '', element = this.each(function() {
293
			if ($(this).attr('type') === 'file') {
294
				var $this = $(this), data = $this.data('filestyle'), options = $.extend({}, $.fn.filestyle.defaults, option, typeof option === 'object' && option);
295
296
				if (!data) {
297
					$this.data('filestyle', ( data = new Filestyle(this, options)));
298
					data.constructor();
299
				}
300
301
				if ( typeof option === 'string') {
302
					get = data[option](value);
303
				}
304
			}
305
		});
306
307
		if ( typeof get !== undefined) {
308
			return get;
309
		} else {
310
			return element;
311
		}
312
	};
313
314
	$.fn.filestyle.defaults = {
315
		'buttonText' : 'Choose file',
316
		'iconName' : 'glyphicon glyphicon-folder-open',
317
		'buttonName' : 'btn-default',
318
		'size' : 'nr',
319
		'input' : true,
320
		'badge' : true,
321
		'icon' : true,
322
		'buttonBefore' : false,
323
		'disabled' : false,
324
		'placeholder': ''
325
	};
326
327
	$.fn.filestyle.noConflict = function() {
328
		$.fn.filestyle = old;
329
		return this;
330
	};
331
332
	$(function() {
333
		$('.filestyle').each(function() {
334
			var $this = $(this), options = {
335
336
				'input' : $this.attr('data-input') === 'false' ? false : true,
337
				'icon' : $this.attr('data-icon') === 'false' ? false : true,
338
				'buttonBefore' : $this.attr('data-buttonBefore') === 'true' ? true : false,
339
				'disabled' : $this.attr('data-disabled') === 'true' ? true : false,
340
				'size' : $this.attr('data-size'),
341
				'buttonText' : $this.attr('data-buttonText'),
342
				'buttonName' : $this.attr('data-buttonName'),
343
				'iconName' : $this.attr('data-iconName'),
344
				'badge' : $this.attr('data-badge') === 'false' ? false : true,
345
				'placeholder': $this.attr('data-placeholder')
346
			};
347
348
			$this.filestyle(options);
349
		});
350
	});
351
})(window.jQuery);
352