gifr.js   B
last analyzed

Complexity

Total Complexity 42
Complexity/F 2.33

Size

Lines of Code 350
Function Count 18

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 2
wmc 42
c 5
b 0
f 0
nc 12
mnd 2
bc 42
fnc 18
dl 0
loc 350
rs 8.295
bpm 2.3333
cpm 2.3333
noi 10

11 Functions

Rating   Name   Duplication   Size   Complexity  
A gifrEls.forEach 0 3 1
A Gfr.frameCount 0 4 1
A Gfr.map 0 3 1
A Gfr.start 0 12 1
A Gfr.setup 0 23 2
A Gfr.assignImage 0 21 1
A Gfr.getFilmstripStyles 0 73 3
A Gfr.coverDimensions 0 16 1
A Gfr.progressFrame 0 18 3
A Gfr.max 0 3 2
A Gfr.positionFrame 0 22 2

How to fix   Complexity   

Complexity

Complex classes like gifr.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
var Gfr = function($el, opts) {
2
  var _this = this;
3
4
  _this.options = $.extend(
5
    {
6
      autoPlay: false,
7
      positioning: "50% 50%" /* String, CSS Background positioning */,
8
      preserveAspectRatio: true,
9
      sizeToFit: true,
10
      $target: jQuery($el.data("target")),
11
      axis: $el.data("axis") || false,
12
      frameDimensions: $el.data("frame").split("x"),
13
      sourceDirection: $el.data("sourceDirection") || "horziontal"
14
    },
15
    opts
16
  );
17
18
  console.log(_this.options);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
19
20
  // Direction
21
  _this.directionOfClip = 1;
22
  _this.$el = $el;
23
  _this.$target = _this.options.$target.length
24
    ? _this.options.$target
25
    : _this.$el;
26
  _this.imgSrc = _this.$el.data("src");
27
  _this.$filmStrip = $(
28
    '<img src="' +
29
      _this.$el.data("src") +
30
      '" class="gifr-filmstrip s__awaiting"/>'
31
  );
32
33
  if (_this.options.frameDimensions) {
34
    var fdims = _this.options.frameDimensions;
35
    (_this.originalFrameWidth = (_this.frameWidth = fdims[
0 ignored issues
show
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
36
      0
37
    ])), (_this.originalFrameHeight = (_this.frameHeight = fdims[1]));
38
  } else {
39
    (_this.originalFrameWidth = (_this.frameWidth = 1920)), (_this.originalFrameHeight = (_this.frameHeight = 698));
0 ignored issues
show
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
40
  }
41
42
  // Specifics
43
  _this.imgRatio = _this.frameWidth / _this.frameHeight;
44
45
  _this.$el.append(_this.$filmStrip);
46
47
  // Bailout no image found!
48
  if (!_this.imgSrc) {
49
    return;
50
  }
51
52
  _this.containerHeight = _this.$target.outerHeight();
53
  _this.containerWidth = _this.$target.outerWidth();
54
55
  _this.counter = 0;
56
  _this.imgPointer = 0;
57
58
  if (_this.options.preserveAspectRatio) {
59
    _this.containerHeight = _this.containerWidth / _this.imgRatio;
60
  }
61
62
  _this.setup();
63
64
  // Element to bind mousemove to?
65
  var $mouseEl = _this.$target || jQuery(document);
66
67
  _this.pMouseX = 0;
68
  _this.pMouseY = 0;
69
  _this.controlAxis = _this.options.axis || "auto";
70
71
  // console.log($mouseEl);
72
73
  $mouseEl.on("mousemove", function(evt) {
74
    clearInterval(_this.timer);
75
    let control = _this.controlAxis;
76
77
    if (control === "auto" && (_this.pMouseX > 0 && _this.pMouseY > 0)) {
78
      var diffX = Math.abs(evt.pageX - _this.pMouseX);
79
      var diffY = Math.abs(evt.pageY - _this.pMouseY);
80
81
      // console.log({x:diffX, y:diffY});
82
      if (diffX === 0 && diffY === 0) {
83
        return;
84
      }
85
86
      if (diffX > diffY) {
87
        _this.controlAxis = (control = "x");
88
      } else {
89
        _this.controlAxis = (control = "y");
90
      }
91
    }
92
93
    _this.pMouseX = evt.pageX;
94
    _this.pMouseY = evt.pageY;
95
96
    var pointer = false;
97
98
    if (control === "x") {
99
      let x = evt.clientX - $mouseEl.offset().left;
100
      pointer = Math.round(
101
        _this.map(x, 0, _this.$target.outerWidth(), 0, _this.frameCount() - 1)
102
      );
103
    } else if (control === "y") {
104
      pointer = Math.round(
105
        _this.map(
106
          evt.clientY,
107
          0,
108
          _this.$target.outerHeight(),
109
          0,
110
          _this.frameCount() - 1
111
        )
112
      );
113
    }
114
115
    if (pointer < 0) {
116
      return false;
117
    }
118
119
    _this.positionFrame(pointer);
120
    _this.$filmStrip.removeClass("s__awaiting");
121
122
    clearTimeout(_this.activity);
123
124
    if (_this.options.autoPlay) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if _this.options.autoPlay 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...
125
      _this.activity = setTimeout(
126
        function() {
127
          _this.start();
128
        },
129
        1000 / 30
130
      );
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
131
    }
132
  });
133
};
134
135
Gfr.prototype.getFilmstripStyles = function() {
136
  var _this = this;
137
  var css = [
138
    "position:absolute",
139
    "top:0",
140
    "left:0",
141
    "max-width:none",
142
    "height: 100%"
143
  ];
144
  var positioning = this.options.positioning.split(" ");
145
146
  if (this.options.sizeToFit) {
147
    var coverDimensions = _this.coverDimensions(
148
      _this.frameWidth,
149
      _this.frameHeight,
150
      _this.$target.outerWidth(),
151
      _this.$target.outerHeight()
152
    );
153
154
    console.log(
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
155
      "Resized Dimensions:",
156
      _this.frameWidth,
157
      _this.frameHeight,
158
      coverDimensions,
159
      _this.frameCount()
160
    );
161
162
    // Increase frame
163
    _this.frameWidth = coverDimensions.width;
164
    _this.frameHeight = coverDimensions.height;
165
    // console.log('Gifr: New Width:', coverDimensions.width, _this.frameCount(), coverDimensions.width * _this.frameCount() );
166
167
    if (_this.options.sourceDirection === "horziontal") {
168
      _this.$filmStrip.attr(
169
        "width",
170
        coverDimensions.width * _this.frameCount()
171
      );
172
      _this.$filmStrip.attr("height", coverDimensions.height);
173
      css.push("height:" + coverDimensions.height + "px");
174
    } else {
175
      console.log("=> Width:", coverDimensions.width);
176
      var filmStripHeight = coverDimensions.height * _this.frameCount();
177
      _this.$filmStrip.attr("width", coverDimensions.width);
178
      _this.$filmStrip.attr("height", filmStripHeight);
179
      css.push("width:" + coverDimensions.width + "px");
180
      css.push("height:" + filmStripHeight + "px");
181
    }
182
  }
183
184
  // X
185
  css.push(
186
    "margin-left: " +
187
      (_this.$el.outerWidth() - _this.frameWidth) *
188
        (parseInt(positioning[0], 10) / 100) +
189
      "px"
190
  );
191
192
  // Y
193
  // console.log('Gifr:', _this.$el.outerHeight(), _this.frameHeight);
194
  css.push(
195
    "margin-top: " +
196
      (_this.$el.outerHeight() - coverDimensions.height) *
0 ignored issues
show
Bug introduced by
The variable coverDimensions does not seem to be initialized in case this.options.sizeToFit on line 146 is false. Are you sure this can never be the case?
Loading history...
197
        (parseInt(positioning[1], 10) / 100) +
198
      "px"
199
  );
200
  console.log(
201
    "Margin top:",
202
    _this.$el.outerHeight(),
203
    coverDimensions.height,
204
    parseInt(positioning[1], 10) / 100
205
  );
206
  return css.join(";");
207
};
208
209
Gfr.prototype.map = function(value, istart, istop, ostart, ostop) {
210
  return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
211
};
212
213
Gfr.prototype.frameCount = function() {
214
  // Normalize to allow for zeroIndex;
215
  return parseInt(this.$el.data("framecount"), 10);
216
};
217
218
Gfr.prototype.setup = function() {
219
  var _this = this;
220
221
  _this.img = new Image();
0 ignored issues
show
Bug introduced by
The variable Image seems to be never declared. If this is a global, consider adding a /** global: Image */ 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...
222
  _this.is_landscape = _this.containerWidth > _this.containerHeight;
223
  _this.sizing = _this.is_landscape
224
    ? _this.containerWidth
225
    : _this.containerHeight;
226
  _this.assignImage(_this.imgSrc);
227
  _this.$filmStrip.attr("style", _this.getFilmstripStyles());
228
229
  _this.$el.css({
230
    overflow: "hidden"
231
  });
232
233
  // Reveal filmstrip
234
  setTimeout(
235
    function() {
236
      _this.$filmStrip.removeClass("s__awaiting");
237
    },
238
    160
239
  );
240
};
241
242
Gfr.prototype.assignImage = function(src) {
243
  var _this = this;
244
245
  _this.img.src = src;
246
247
  this.img.onload = function() {
248
    setTimeout(
249
      function() {
250
        _this.$el.fadeIn(100);
251
      },
252
      500
253
    );
254
255
    _this.gifHeight = _this.sizing;
256
    _this.gifWidth = _this.frameWidth * _this.frameCount();
257
258
    if (_this.options.autoPlay) {
259
      _this.start();
260
    }
261
  };
262
};
263
264
Gfr.prototype.start = function() {
265
  var _this = this;
266
267
  clearInterval(_this.timer);
268
269
  _this.timer = setInterval(
270
    function() {
271
      _this.progressFrame();
272
    },
273
    1000 / 8
274
  );
275
};
276
277
Gfr.prototype.positionFrame = function(numberOfFrames) {
278
  var _this = this;
279
280
  _this.counter = numberOfFrames;
281
282
  if (_this.options.sourceDirection === "horziontal") {
283
    _this.$filmStrip.css(
284
      "transform",
285
      "translateX(" + _this.frameWidth * _this.counter * -1 + "px)"
286
    );
287
  } else {
288
    console.log(
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
289
      "Frame size:",
290
      `${_this.frameWidth} x ${_this.frameHeight}`,
291
      _this.frameHeight * _this.counter * -1
292
    );
293
    _this.$filmStrip.css(
294
      "transform",
295
      "translateY(" + _this.frameHeight * _this.counter * -1 + "px)"
296
    );
297
  }
298
};
299
300
Gfr.prototype.progressFrame = function() {
301
  var _this = this;
302
303
  _this.positionFrame(_this.counter);
304
305
  if (_this.counter < _this.frameCount()) {
306
    //   _this.counter += _this.directionOfClip;
307
  } else {
308
    _this.directionOfClip *= -1;
309
    //   _this.counter = 0;
310
  }
311
312
  if (_this.counter < 1) {
313
    _this.directionOfClip = 1;
314
  }
315
316
  _this.counter += _this.directionOfClip;
317
};
318
319
Gfr.prototype.coverDimensions = function(
320
  childWidth,
321
  childHeight,
322
  containerWidth,
323
  containerHeight
324
) {
325
  var scaleFactor = this.max(
326
    containerWidth / childWidth,
327
    containerHeight / childHeight
328
  );
329
330
  return {
331
    width: Math.ceil(childWidth * scaleFactor),
332
    height: Math.ceil(childHeight * scaleFactor)
333
  };
334
};
335
336
Gfr.prototype.max = function(a, b) {
337
  return a > b ? a : b;
338
};
339
340
if (window.module) {
341
  module.exports = Gfr;
342
} else {
343
  var gifrEls = document.querySelectorAll(".gif");
344
345
  if (gifrEls) {
346
    gifrEls.forEach(function(el) {
347
      new Gfr(jQuery(el));
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new Gfr(jQuery(el)) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
348
    });
349
  }
350
}
351