Passed
Push — master ( 4cff34...ec9704 )
by Sylvain
01:29
created

jquery.images-compare.js ➔ stringRepeat   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
nc 3
nop 2
dl 0
loc 9
rs 9.6666
c 1
b 0
f 0
1
/** global: Hammer */
2
;(function ($, window) {
3
    "use strict";
4
5
    function stringRepeat(s, precision) {
6
        // String repeat polyfill
7
        if (!String.prototype.repeat) {
8
            precision = precision || 1;
9
            return new Array(precision + 1).join(s);
10
        } else {
11
            return s.repeat(precision);
12
        }
13
    }
14
15
    var pluginName = 'imagesCompare',
16
        defaults = {
17
            initVisibleRatio: 0.5,
18
            interactionMode: "drag", // "drag", "mousemove", "click"
19
            animationDuration: 400, // default animation duration in ms
20
            animationEasing: "swing",
21
            addSeparator: true, // add a html element on the separation
22
            addDragHandle: true, // add a html drag handle element on the separation
23
            precision: 4
24
        };
25
26
    // Our object, using revealing module pattern
27
    function ImagesCompare(element, options) {
28
        element = $(element);
29
        options = $.extend({}, defaults, options);
30
        options.roundFactor = parseInt('1' + stringRepeat('0', options.precision));
31
32
        this._name = pluginName;
33
34
        var frontElement, backElement, separator, dragHandle, lastRatio = 1, size = {
35
            width: 0,
36
            height: 0,
37
            maxWidth: 0,
38
            maxHeight: 0
39
        }, events = {
40
            initialised: "imagesCompare:initialised",
41
            changed: "imagesCompare:changed",
42
            resized: "imagesCompare:resized"
43
        };
44
45
        init();
46
47
        function init() {
48
            updateDom();
49
            patchSize();
50
            initInteractions();
51
52
            $(frontElement).attr('ratio', options.initVisibleRatio);
53
            setVisibleRatio(options.initVisibleRatio);
54
55
            // Let the world know we have done the init
56
            element.trigger({
57
                type: events.initialised
58
            });
59
        }
60
61
        function addResize() {
62
            $(window).on('resize', function (event) {
63
                frontElement.css('clip', '');
64
                patchSize();
65
                setVisibleRatio(lastRatio);
66
67
                // Let the world know we have done some resize updates
68
                element.trigger({
69
                    type: events.resized,
70
                    originalEvent: event
71
                });
72
            });
73
        }
74
75
        function initInteractions() {
76
            options.interactionMode = options.interactionMode.toLowerCase();
77
78
            if (options.interactionMode != "drag" && options.interactionMode != "mousemove" && options.interactionMode != "click") {
79
                console.warn('No valid interactionMode found, valid values are "drag", "mousemove", "click"');
80
            }
81
82
            switch (options.interactionMode) {
83
                case "drag":
84
                    initDrag();
85
                    break;
86
                case "mousemove":
87
                    initMouseMove();
88
                    break;
89
                case "click":
90
                    initClick();
91
                    break;
92
                default:
93
                    initDrag();
94
            }
95
        }
96
97
        function initDrag() {
98
            if (typeof Hammer == 'undefined') {
99
                console.error('Please include the hammerjs library for drag support');
100
            }
101
            addDrag();
102
            addResize();
103
        }
104
105
        function initMouseMove() {
106
            addMouseMove();
107
            addResize();
108
        }
109
110
        function initClick() {
111
            addClick();
112
            addResize();
113
        }
114
115
        function addClick() {
116
            element.on('click', function (event) {
117
                var ratio = getElementRatio(event.pageX);
118
                setVisibleRatio(ratio);
119
            });
120
        }
121
122
        function addMouseMove() {
123
            var lastMove = 0;
124
            var eventThrottle = 1;
125
            element.on('mousemove', function (event) {
126
                event.preventDefault();
127
                var now = Date.now();
128
                if (now > lastMove + eventThrottle) {
129
                    lastMove = now;
130
                    var ratio = getElementRatio(event.pageX);
131
                    setVisibleRatio(ratio);
132
                }
133
            });
134
135
            element.on('mouseout', function (event) {
136
                var ratio = getElementRatio(event.pageX);
137
                setVisibleRatio(ratio);
138
            });
139
        }
140
141
        function addDrag() {
142
            var hammertime = new Hammer(element[0]);
143
            hammertime.get('pan').set({direction: Hammer.DIRECTION_HORIZONTAL});
144
            hammertime.on('pan', function (event) {
145
                var ratio = getElementRatio(event.srcEvent.pageX);
146
                setVisibleRatio(ratio);
147
            });
148
        }
149
150
        function updateDom() {
151
            element.addClass('images-compare-container');
152
            element.css('display', 'inline-block');
153
154
            frontElement = element.find('> *:nth-child(1)');
155
            backElement = element.find('> *:nth-child(2)');
156
157
            frontElement.addClass("images-compare-before");
158
            frontElement.css('display', 'block');
159
            backElement.addClass("images-compare-after");
160
            backElement.css('display', 'block');
161
162
            if (options.addDragHandle) {
163
                buildDragHandle();
164
            }
165
166
            if (options.addSeparator) {
167
                buildSeparator();
168
            }
169
        }
170
171
        function buildSeparator() {
172
            element.prepend("<div class='images-compare-separator'></div>");
173
            separator = element.find(".images-compare-separator");
174
175
        }
176
177
        function buildDragHandle() {
178
            element.prepend("<div class='images-compare-handle'></div>");
179
            dragHandle = element.find(".images-compare-handle");
180
            dragHandle.append("<span class='images-compare-left-arrow'></span>");
181
            dragHandle.append("<span class='images-compare-right-arrow'></span>");
182
        }
183
184
        function patchSize() {
185
            var imgRef = backElement.find('img').first();
186
            setSize(imgRef.width(), imgRef.height(), imgRef.naturalWidth(), imgRef.naturalHeight());
187
            element.css('max-width', size.maxWidth + 'px');
188
            element.css('max-height', size.maxHeight + 'px');
189
            frontElement.width(size.width);
190
            frontElement.height(size.height);
191
        }
192
193
        /**
194
         *
195
         * @param x
196
         * @return float
197
         */
198
        function getElementRatio(x) {
199
            return roundRatio((x - element.offset().left) / frontElement.width());
200
        }
201
202
        /**
203
         *
204
         * @param ratio
205
         * @return float
206
         */
207
        function roundRatio(ratio) {
208
            ratio = Math.round((ratio * options.roundFactor)) / options.roundFactor;
209
            if (ratio > 1) {
210
                ratio = 1;
211
            }
212
213
            if (ratio < 0) {
214
                ratio = 0;
215
            }
216
217
            return ratio;
218
219
        }
220
221
        /**
222
         * Animation request
223
         *
224
         * @param startValue float
225
         * @param endValue float
226
         * @param duration value in ms
227
         * @param easing linear or swing
228
         */
229 View Code Duplication
        function launchAnimation(startValue, endValue, duration, easing) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
230
            $(frontElement).attr('ratio', startValue).animate({ratio: startValue}, {
231
                duration: 0
232
            });
233
234
            $(frontElement).stop().attr('ratio', startValue).animate({ratio: endValue}, {
235
                duration: duration,
236
                easing: easing,
237
                step: function (now) {
238
                    var width = getRatioValue(now);
239
                    lastRatio = now;
240
                    frontElement.attr('ratio', now).css('clip', 'rect(0, ' + width + 'px, ' + size.height + 'px, 0)');
241
242
                    if (options.addSeparator) {
243
                        separator.css('left', width + 'px');
244
                    }
245
246
                    if (options.addDragHandle) {
247
                        dragHandle.css('left', width + 'px');
248
                    }
249
                },
250
                done: function (animation, jumpedToEnd) {
251
                    var ratio = $(frontElement).attr('ratio');
252
                    // Let the world know something has changed
253
                    element.trigger({
254
                        type: events.changed,
255
                        ratio: ratio,
256
                        value: getRatioValue(ratio),
257
                        animate: true,
258
                        animation : animation,
259
                        jumpedToEnd: jumpedToEnd
260
                    });
261
                }
262
            });
263
        }
264
265
        /**
266
         * Get value to reach, based on a ratio
267
         *
268
         * @param ratio float
269
         * @return {number}
270
         */
271
        function getRatioValue(ratio) {
272
            ratio = Math.round((ratio * options.roundFactor)) / options.roundFactor;
273
            return Math.round(frontElement.width() * ratio);
274
        }
275
276
        /**
277
         * Change visible ratio
278
         *
279
         * @param ratio float
280
         * @param animate boolean Do we want an animation ?
281
         * @param duration in ms
282
         * @param easing 'swing', 'linear'
283
         */
284 View Code Duplication
        function setVisibleRatio(ratio, animate, duration, easing) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
285
            if (typeof animate == 'undefined') {
286
                animate = false;
287
            }
288
289
            var width = getRatioValue(ratio);
290
291
            if (animate) {
292
                var finalDuration = duration ? duration : options.animationDuration;
293
                var finalEasing = easing ? easing : options.animationEasing;
294
295
                launchAnimation(lastRatio, ratio, finalDuration, finalEasing);
296
297
                // Let the world know something has changed
298
                if (lastRatio != ratio) {
299
                    element.trigger({
300
                        type: events.changed,
301
                        ratio: lastRatio,
302
                        value: width,
303
                        animate: animate
304
                    });
305
                }
306
307
                return;
308
309
            } else {
310
                frontElement.stop().css('clip', 'rect(0, ' + width + 'px, ' + size.height + 'px, 0)');
311
312
                if (options.addSeparator) {
313
                    $(separator).stop().css('left', width + 'px');
314
                }
315
316
                if (options.addDragHandle) {
317
                    dragHandle.css('left', width + 'px');
318
                }
319
            }
320
321
            // Let the world know something has changed
322
            if (lastRatio != ratio) {
323
                element.trigger({
324
                    type: events.changed,
325
                    ratio: ratio,
326
                    value: width,
327
                    animate: animate
328
                });
329
            }
330
331
            lastRatio = ratio;
332
        }
333
334
        function setSize(width, height, maxWidth, maxHeight) {
335
            if (typeof width != 'undefined') {
336
                setWidth(width);
337
            }
338
            if (typeof height != 'undefined') {
339
                setHeight(height);
340
            }
341
            if (typeof maxWidth != 'undefined') {
342
                setMaxWidth(maxWidth);
343
            }
344
            if (typeof maxHeight != 'undefined') {
345
                setMaxHeight(maxHeight);
346
            }
347
            return size;
348
        }
349
350
        function setWidth(width) {
351
            size.width = width;
352
            return size;
353
        }
354
355
        function setMaxWidth(maxWidth) {
356
            size.maxWidth = maxWidth;
357
            return size;
358
        }
359
360
        function setHeight(height) {
361
            size.height = height;
362
            return size;
363
        }
364
365
        function setMaxHeight(maxHeight) {
366
            size.maxHeight = maxHeight;
367
            return size;
368
        }
369
370
        // public function declaration
371
        // returning element to preserve chaining
372
        return {
373
            "setValue": function (ratio, animate, duration, easing) {
374
                setVisibleRatio(ratio, animate, duration, easing);
375
                return element;
376
            },
377
            "getValue": function () {
378
                return lastRatio;
379
            },
380
            "on": function (eventName, callback) {
381
                element.on(eventName, callback);
382
                return element;
383
            },
384
            "off": function (eventName, callback) {
385
                element.off(eventName, callback);
386
                return element;
387
            },
388
            "events": function () {
389
                return events;
390
            }
391
        };
392
    }
393
394
395
    /**
396
     * Plugin declaration
397
     *
398
     * @param userOptions
399
     * @return {*}
400
     */
401
    $.fn.imagesCompare = function (userOptions) {
402
        var options = $.extend(defaults, userOptions);
403
        return this.each(function () {
404
            if (!$.data(this, pluginName)) {
405
                $.data(this, pluginName, new ImagesCompare(this, options));
406
            }
407
        });
408
    };
409
410
})(jQuery, window, document);
411
412
// http://www.jacklmoore.com/notes/naturalwidth-and-naturalheight-in-ie/
413
(function ($) {
414
    var props = ['Width', 'Height'], prop, propsLength;
415
416
    propsLength = props.length;
417
418
    for (var index = 0; index < propsLength; index++) {
419
        prop = props[index];
420
        /*jslint loopfunc: true */
421
        (function (natural, prop) {
422
            $.fn[natural] = (natural in document.createElement('img')) ?
423
                function () {
424
                    return this[0][natural];
425
                } :
426
                function () {
427
                    var
428
                        node = this[0],
429
                        img,
430
                        value = 0;
431
432
                    if (node.tagName.toLowerCase() === 'img') {
433
                        img = document.createElement('img');
434
                        img.src = node.src;
435
                        value = img[prop];
436
                    }
437
                    return value;
438
                };
439
        }('natural' + prop, prop.toLowerCase()));
440
        /*jslint loopfunc: false */
441
    }
442
}(jQuery));
443