Completed
Push — master ( e6718f...1f82f0 )
by Chad
18s queued 16s
created

docs/js/jquery.iviewer.js   F

Complexity

Total Complexity 152
Complexity/F 1.85

Size

Lines of Code 1156
Function Count 82

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 545
c 0
b 0
f 0
dl 0
loc 1156
rs 2
wmc 152
mnd 70
bc 70
fnc 82
bpm 0.8536
cpm 1.8536
noi 33

3 Functions

Rating   Name   Duplication   Size   Complexity  
A jquery.iviewer.js ➔ mul 0 1 1
A jquery.iviewer.js ➔ makeMouseEvent 0 15 2
F jquery.iviewer.js ➔ div 0 1 75

How to fix   Complexity   

Complexity

Complex classes like docs/js/jquery.iviewer.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
 * iviewer Widget for jQuery UI
3
 * https://github.com/can3p/iviewer
4
 *
5
 * Copyright (c) 2009 - 2012 Dmitry Petrov
6
 * Dual licensed under the MIT and GPL licenses.
7
 *  - http://www.opensource.org/licenses/mit-license.php
8
 *  - http://www.gnu.org/copyleft/gpl.html
9
 *
10
 * Author: Dmitry Petrov
11
 * Version: 0.7.7
12
 */
13
14
( function( $, undefined ) {
15
16
//this code was taken from the https://github.com/furf/jquery-ui-touch-punch
17
var mouseEvents = {
18
        touchstart: 'mousedown',
19
        touchmove: 'mousemove',
20
        touchend: 'mouseup'
21
    },
22
    gesturesSupport = 'ongesturestart' in document.createElement('div');
23
24
25
/**
26
 * Convert a touch event to a mouse-like
27
 */
28
function makeMouseEvent (event) {
29
    var touch = event.originalEvent.changedTouches[0];
30
31
    return $.extend(event, {
32
        type:    mouseEvents[event.type],
33
        which:   1,
34
        pageX:   touch.pageX,
35
        pageY:   touch.pageY,
36
        screenX: touch.screenX,
37
        screenY: touch.screenY,
38
        clientX: touch.clientX,
39
        clientY: touch.clientY,
40
        isTouchEvent: true
41
    });
42
}
43
44
var mouseProto = $.ui.mouse.prototype,
45
    _mouseInit = $.ui.mouse.prototype._mouseInit;
46
47
mouseProto._mouseInit = function() {
48
    var self = this;
49
    self._touchActive = false;
50
51
    this.element.bind( 'touchstart.' + this.widgetName, function(event) {
52
        if (gesturesSupport && event.originalEvent.touches.length > 1) { return; }
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
53
        self._touchActive = true;
54
        return self._mouseDown(makeMouseEvent(event));
55
    })
56
57
    var self = this;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable self already seems to be declared on line 48. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
58
    // these delegates are required to keep context
59
    this._mouseMoveDelegate = function(event) {
60
        if (gesturesSupport && event.originalEvent.touches && event.originalEvent.touches.length > 1) { return; }
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
61
        if (self._touchActive) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if self._touchActive 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
            return self._mouseMove(makeMouseEvent(event));
63
        }
64
    };
65
    this._mouseUpDelegate = function(event) {
66
        if (self._touchActive) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if self._touchActive 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...
67
            self._touchActive = false;
68
            return self._mouseUp(makeMouseEvent(event));
69
        }
70
    };
71
72
    $(document)
73
        .bind('touchmove.'+ this.widgetName, this._mouseMoveDelegate)
74
        .bind('touchend.' + this.widgetName, this._mouseUpDelegate);
75
76
    _mouseInit.apply(this);
77
}
78
79
/**
80
 * Simple implementation of jQuery like getters/setters
81
 * var val = something();
82
 * something(val);
83
 */
84
var setter = function(setter, getter) {
85
    return function(val) {
0 ignored issues
show
Unused Code introduced by
The parameter val is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
86
        if (arguments.length === 0) {
87
            return getter.apply(this);
88
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
89
            setter.apply(this, arguments);
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...
90
        }
91
    }
92
};
93
94
/**
95
 * Internet explorer rotates image relative left top corner, so we should
96
 * shift image when it's rotated.
97
 */
98
var ieTransforms = {
99
        '0': {
100
            marginLeft: 0,
101
            marginTop: 0,
102
            filter: 'progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0, M21=0, M22=1, SizingMethod="auto expand")'
103
        },
104
105
        '90': {
106
            marginLeft: -1,
107
            marginTop: 1,
108
            filter: 'progid:DXImageTransform.Microsoft.Matrix(M11=0, M12=-1, M21=1, M22=0, SizingMethod="auto expand")'
109
        },
110
111
        '180': {
112
            marginLeft: 0,
113
            marginTop: 0,
114
            filter: 'progid:DXImageTransform.Microsoft.Matrix(M11=-1, M12=0, M21=0, M22=-1, SizingMethod="auto expand")'
115
        },
116
117
        '270': {
118
            marginLeft: -1,
119
            marginTop: 1,
120
            filter: 'progid:DXImageTransform.Microsoft.Matrix(M11=0, M12=1, M21=-1, M22=0, SizingMethod="auto expand")'
121
        }
122
    },
123
    // this test is the inversion of the css filters test from the modernizr project
124
    useIeTransforms = function() {
125
        var modElem = document.createElement('modernizr'),
126
		mStyle = modElem.style,
127
		omPrefixes = 'Webkit Moz O ms',
128
		domPrefixes = omPrefixes.toLowerCase().split(' '),
129
        	props = ("transform" + ' ' + domPrefixes.join("Transform ") + "Transform").split(' ');
130
        for ( var i in props ) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
131
            var prop = props[i];
132
            if ( !$.contains(prop, "-") && mStyle[prop] !== undefined ) {
133
                return false;
134
            }
135
        }
136
        return true;
137
    }();
138
139
$.widget( "ui.iviewer", $.ui.mouse, {
140
    widgetEventPrefix: "iviewer",
141
    options : {
142
        /**
143
        * start zoom value for image, not used now
144
        * may be equal to "fit" to fit image into container or scale in %
145
        **/
146
        zoom: "fit",
147
        /**
148
        * base value to scale image
149
        **/
150
        zoom_base: 100,
151
        /**
152
        * maximum zoom
153
        **/
154
        zoom_max: 800,
155
        /**
156
        * minimum zoom
157
        **/
158
        zoom_min: 25,
159
        /**
160
        * base of rate multiplier.
161
        * zoom is calculated by formula: zoom_base * zoom_delta^rate
162
        **/
163
        zoom_delta: 1.4,
164
        /**
165
        * whether the zoom should be animated.
166
        */
167
        zoom_animation: true,
168
        /**
169
        * if true plugin doesn't add its own controls
170
        **/
171
        ui_disabled: false,
172
        /**
173
         * If false mousewheel will be disabled
174
         */
175
        mousewheel: true,
176
        /**
177
        * if false, plugin doesn't bind resize event on window and this must
178
        * be handled manually
179
        **/
180
        update_on_resize: true,
181
        /**
182
        * event is triggered when zoom value is changed
183
        * @param int new zoom value
184
        * @return boolean if false zoom action is aborted
185
        **/
186
        onZoom: jQuery.noop,
187
        /**
188
        * event is triggered when zoom value is changed after image is set to the new dimensions
189
        * @param int new zoom value
190
        * @return boolean if false zoom action is aborted
191
        **/
192
        onAfterZoom: jQuery.noop,
193
        /**
194
        * event is fired on drag begin
195
        * @param object coords mouse coordinates on the image
196
        * @return boolean if false is returned, drag action is aborted
197
        **/
198
        onStartDrag: jQuery.noop,
199
        /**
200
        * event is fired on drag action
201
        * @param object coords mouse coordinates on the image
202
        **/
203
        onDrag: jQuery.noop,
204
        /**
205
        * event is fired on drag stop
206
        * @param object coords mouse coordinates on the image
207
        **/
208
        onStopDrag: jQuery.noop,
209
        /**
210
        * event is fired when mouse moves over image
211
        * @param object coords mouse coordinates on the image
212
        **/
213
        onMouseMove: jQuery.noop,
214
        /**
215
        * mouse click event
216
        * @param object coords mouse coordinates on the image
217
        **/
218
        onClick: jQuery.noop,
219
        /**
220
        * event is fired when image starts to load
221
        */
222
        onStartLoad: null,
223
        /**
224
        * event is fired, when image is loaded and initially positioned
225
        */
226
        onFinishLoad: null,
227
        /**
228
        * event is fired when image load error occurs
229
        */
230
        onErrorLoad: null
231
    },
232
233
    _create: function() {
234
        var me = this;
235
236
        //drag variables
237
        this.dx = 0;
238
        this.dy = 0;
239
240
        /* object containing actual information about image
241
        *   @img_object.object - jquery img object
242
        *   @img_object.orig_{width|height} - original dimensions
243
        *   @img_object.display_{width|height} - actual dimensions
244
        */
245
        this.img_object = {};
246
247
        this.zoom_object = {}; //object to show zoom status
248
249
        this._angle = 0;
250
251
        this.current_zoom = this.options.zoom;
252
253
        if(this.options.src === null){
254
            return;
255
        }
256
257
        this.container = this.element;
258
259
        this._updateContainerInfo();
260
261
        //init container
262
        this.container.css("overflow","hidden");
263
264
        if (this.options.update_on_resize == true) {
0 ignored issues
show
Best Practice introduced by
Comparing this.options.update_on_resize to true using the == operator is not safe. Consider using === instead.
Loading history...
265
            $(window).resize(function() {
266
                me.update();
267
            });
268
        }
269
270
        this.img_object = new $.ui.iviewer.ImageObject(this.options.zoom_animation);
271
272
        if (this.options.mousewheel) {
273
            this.container.bind('mousewheel.iviewer', function(ev, delta)
274
                {
275
                    //this event is there instead of containing div, because
276
                    //at opera it triggers many times on div
277
                    var zoom = (delta > 0)?1:-1,
278
                        container_offset = me.container.offset(),
279
                        mouse_pos = {
280
                            //jquery.mousewheel 3.1.0 uses strange MozMousePixelScroll event
281
                            //which is not being fixed by jQuery.Event
282
                            x: (ev.pageX || ev.originalEvent.pageX) - container_offset.left,
283
                            y: (ev.pageY || ev.originalEvent.pageX) - container_offset.top
284
                        };
285
286
                    me.zoom_by(zoom, mouse_pos);
287
                    return false;
288
                });
289
290
            if (gesturesSupport) {
291
                var gestureThrottle = +new Date();
292
                var originalScale, originalCenter;
293
                this.img_object.object()
294
                    // .bind('gesturestart', function(ev) {
295
                    .bind('touchstart', function(ev) {
296
                        originalScale = me.current_zoom;
297
                        var touches = ev.originalEvent.touches,
298
                            container_offset;
299
                        if (touches.length == 2) {
300
                            container_offset = me.container.offset();
301
                            originalCenter = {
302
                                x: (touches[0].pageX + touches[1].pageX) / 2  - container_offset.left,
303
                                y: (touches[0].pageY + touches[1].pageY) / 2 - container_offset.top
304
                            };
305
                        } else {
306
                            originalCenter = null;
307
                        }
308
                    }).bind('gesturechange', function(ev) {
309
                        //do not want to import throttle function from underscore
310
                        var d = +new Date();
311
                        if ((d - gestureThrottle) < 50) { return; }
312
                        gestureThrottle = d;
313
                        var zoom = originalScale * ev.originalEvent.scale;
314
                        me.set_zoom(zoom, originalCenter);
315
                        ev.preventDefault();
316
                    }).bind('gestureend', function(ev) {
0 ignored issues
show
Unused Code introduced by
The parameter ev is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
317
                        originalCenter = null;
318
                    });
319
            }
320
        }
321
322
        //init object
323
        this.img_object.object()
324
            //bind mouse events
325
            .click(function(e){return me._click(e)})
326
                .prependTo(this.container);
327
328
        this.container.bind('mousemove', function(ev) { me._handleMouseMove(ev); });
329
330
        this.loadImage(this.options.src);
331
332
        if(!this.options.ui_disabled)
333
        {
334
            this.createui();
335
        }
336
337
        this._mouseInit();
338
    },
339
340
    destroy: function() {
341
        $.Widget.prototype.destroy.call( this );
342
        this._mouseDestroy();
343
        this.img_object.object().remove();
344
        this.container.off('.iviewer');
345
        this.container.css('overflow', ''); //cleanup styles on destroy
346
    },
347
348
    _updateContainerInfo: function()
349
    {
350
        this.options.height = this.container.height();
351
        this.options.width = this.container.width();
352
    },
353
354
    update: function()
355
    {
356
        this._updateContainerInfo()
357
        this.setCoords(this.img_object.x(), this.img_object.y());
358
    },
359
360
    loadImage: function( src )
361
    {
362
        this.current_zoom = this.options.zoom;
363
        var me = this;
364
365
        this._trigger('onStartLoad', 0, src);
366
367
        this.container.addClass("iviewer_loading");
368
        this.img_object.load(src, function() {
369
            me._imageLoaded(src);
370
        }, function() {
371
            me._trigger("onErrorLoad", 0, src);
372
        });
373
    },
374
375
    _imageLoaded: function(src) {
376
        this.container.removeClass("iviewer_loading");
377
        this.container.addClass("iviewer_cursor");
378
379
        if(this.options.zoom == "fit"){
380
            this.fit(true);
381
        }
382
        else {
383
            this.set_zoom(this.options.zoom, true);
384
        }
385
386
        this._trigger('onFinishLoad', 0, src);
387
    },
388
389
    /**
390
    * fits image in the container
391
    *
392
    * @param {boolean} skip_animation
393
    **/
394
    fit: function(skip_animation)
395
    {
396
        var aspect_ratio = this.img_object.orig_width() / this.img_object.orig_height();
397
        var window_ratio = this.options.width /  this.options.height;
398
        var choose_left = (aspect_ratio > window_ratio);
399
        var new_zoom = 0;
400
401
        if(choose_left){
402
            new_zoom = this.options.width / this.img_object.orig_width() * 100;
403
        }
404
        else {
405
            new_zoom = this.options.height / this.img_object.orig_height() * 100;
406
        }
407
408
      this.set_zoom(new_zoom, skip_animation);
409
    },
410
411
    /**
412
    * center image in container
413
    **/
414
    center: function()
415
    {
416
        this.setCoords(-Math.round((this.img_object.display_width() - this.options.width)/2),
417
                -Math.round((this.img_object.display_height() - this.options.height)/2));
418
    },
419
420
    /**
421
    *   move a point in container to the center of display area
422
    *   @param x a point in container
423
    *   @param y a point in container
424
    **/
425
    moveTo: function(x, y)
426
    {
427
        var dx = x-Math.round(this.options.width/2);
428
        var dy = y-Math.round(this.options.height/2);
429
430
        var new_x = this.img_object.x() - dx;
431
        var new_y = this.img_object.y() - dy;
432
433
        this.setCoords(new_x, new_y);
434
    },
435
436
    /**
437
     * Get container offset object.
438
     */
439
    getContainerOffset: function() {
440
        return jQuery.extend({}, this.container.offset());
441
    },
442
443
    /**
444
    * set coordinates of upper left corner of image object
445
    **/
446
    setCoords: function(x,y)
447
    {
448
        //do nothing while image is being loaded
449
        if(!this.img_object.loaded()) { return; }
450
451
        var coords = this._correctCoords(x,y);
452
        this.img_object.x(coords.x);
453
        this.img_object.y(coords.y);
454
    },
455
456
    _correctCoords: function( x, y )
457
    {
458
        x = parseInt(x, 10);
459
        y = parseInt(y, 10);
460
461
        //check new coordinates to be correct (to be in rect)
462
        if(y > 0){
463
            y = 0;
464
        }
465
        if(x > 0){
466
            x = 0;
467
        }
468
        if(y + this.img_object.display_height() < this.options.height){
469
            y = this.options.height - this.img_object.display_height();
470
        }
471
        if(x + this.img_object.display_width() < this.options.width){
472
            x = this.options.width - this.img_object.display_width();
473
        }
474
        if(this.img_object.display_width() <= this.options.width){
475
            x = -(this.img_object.display_width() - this.options.width)/2;
476
        }
477
        if(this.img_object.display_height() <= this.options.height){
478
            y = -(this.img_object.display_height() - this.options.height)/2;
479
        }
480
481
        return { x: x, y:y };
482
    },
483
484
485
    /**
486
    * convert coordinates on the container to the coordinates on the image (in original size)
487
    *
488
    * @return object with fields x,y according to coordinates or false
489
    * if initial coords are not inside image
490
    **/
491
    containerToImage : function (x,y)
492
    {
493
        var coords = { x : x - this.img_object.x(),
494
                 y :  y - this.img_object.y()
495
        };
496
497
        coords = this.img_object.toOriginalCoords(coords);
498
499
        return { x :  util.descaleValue(coords.x, this.current_zoom),
500
                 y :  util.descaleValue(coords.y, this.current_zoom)
501
        };
502
    },
503
504
    /**
505
    * convert coordinates on the image (in original size, and zero angle) to the coordinates on the container
506
    *
507
    * @return object with fields x,y according to coordinates
508
    **/
509
    imageToContainer : function (x,y)
510
    {
511
        var coords = {
512
                x : util.scaleValue(x, this.current_zoom),
513
                y : util.scaleValue(y, this.current_zoom)
514
            };
515
516
        return this.img_object.toRealCoords(coords);
517
    },
518
519
    /**
520
    * get mouse coordinates on the image
521
    * @param e - object containing pageX and pageY fields, e.g. mouse event object
522
    *
523
    * @return object with fields x,y according to coordinates or false
524
    * if initial coords are not inside image
525
    **/
526
    _getMouseCoords : function(e)
527
    {
528
        var containerOffset = this.container.offset();
529
            coords = this.containerToImage(e.pageX - containerOffset.left, e.pageY - containerOffset.top);
0 ignored issues
show
Bug introduced by
The variable coords seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.coords.
Loading history...
530
531
        return coords;
532
    },
533
534
    /**
535
    * set image scale to the new_zoom
536
    *
537
    * @param {number} new_zoom image scale in %
538
    * @param {boolean} skip_animation
539
    * @param {x: number, y: number} Coordinates of point the should not be moved on zoom. The default is the center of image.
540
    **/
541
    set_zoom: function(new_zoom, skip_animation, zoom_center)
542
    {
543
        if (this._trigger('onZoom', 0, new_zoom) == false) {
0 ignored issues
show
Best Practice introduced by
Comparing this._trigger("onZoom", 0, new_zoom) to false using the == operator is not safe. Consider using === instead.
Loading history...
544
            return;
545
        }
546
547
        //do nothing while image is being loaded
548
        if(!this.img_object.loaded()) { return; }
549
550
        zoom_center = zoom_center || {
551
            x: Math.round(this.options.width/2),
552
            y: Math.round(this.options.height/2)
553
        }
554
555
        if(new_zoom <  this.options.zoom_min)
556
        {
557
            new_zoom = this.options.zoom_min;
558
        }
559
        else if(new_zoom > this.options.zoom_max)
560
        {
561
            new_zoom = this.options.zoom_max;
562
        }
563
564
        /* we fake these values to make fit zoom properly work */
565
        if(this.current_zoom == "fit")
566
        {
567
            var old_x = zoom_center.x + Math.round(this.img_object.orig_width()/2);
568
            var old_y = zoom_center.y + Math.round(this.img_object.orig_height()/2);
569
            this.current_zoom = 100;
570
        }
571
        else {
572
            var old_x = -this.img_object.x() + zoom_center.x;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable old_x already seems to be declared on line 567. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
573
            var old_y = -this.img_object.y() + zoom_center.y
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable old_y already seems to be declared on line 568. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
574
        }
575
576
        var new_width = util.scaleValue(this.img_object.orig_width(), new_zoom);
577
        var new_height = util.scaleValue(this.img_object.orig_height(), new_zoom);
578
        var new_x = util.scaleValue( util.descaleValue(old_x, this.current_zoom), new_zoom);
579
        var new_y = util.scaleValue( util.descaleValue(old_y, this.current_zoom), new_zoom);
580
581
        new_x = zoom_center.x - new_x;
582
        new_y = zoom_center.y - new_y;
583
584
        new_width = Math.floor(new_width);
585
        new_height = Math.floor(new_height);
586
        new_x = Math.floor(new_x);
587
        new_y = Math.floor(new_y);
588
589
        this.img_object.display_width(new_width);
590
        this.img_object.display_height(new_height);
591
592
        var coords = this._correctCoords( new_x, new_y ),
593
            self = this;
594
595
        this.img_object.setImageProps(new_width, new_height, coords.x, coords.y,
596
                                        skip_animation, function() {
597
            self._trigger('onAfterZoom', 0, new_zoom );
598
        });
599
        this.current_zoom = new_zoom;
600
601
        this.update_status();
602
    },
603
604
    /**
605
    * changes zoom scale by delta
606
    * zoom is calculated by formula: zoom_base * zoom_delta^rate
607
    * @param Integer delta number to add to the current multiplier rate number
608
    * @param {x: number, y: number=} Coordinates of point the should not be moved on zoom.
609
    **/
610
    zoom_by: function(delta, zoom_center)
611
    {
612
        var closest_rate = this.find_closest_zoom_rate(this.current_zoom);
613
614
        var next_rate = closest_rate + delta;
615
        var next_zoom = this.options.zoom_base * Math.pow(this.options.zoom_delta, next_rate)
616
        if(delta > 0 && next_zoom < this.current_zoom)
617
        {
618
            next_zoom *= this.options.zoom_delta;
619
        }
620
621
        if(delta < 0 && next_zoom > this.current_zoom)
622
        {
623
            next_zoom /= this.options.zoom_delta;
624
        }
625
626
        this.set_zoom(next_zoom, undefined, zoom_center);
627
    },
628
629
    /**
630
    * Rotate image
631
    * @param {num} deg Degrees amount to rotate. Positive values rotate image clockwise.
632
    *     Currently 0, 90, 180, 270 and -90, -180, -270 values are supported
633
    *
634
    * @param {boolean} abs If the flag is true if, the deg parameter will be considered as
635
    *     a absolute value and relative otherwise.
636
    * @return {num|null} Method will return current image angle if called without any arguments.
637
    **/
638
    angle: function(deg, abs) {
639
        if (arguments.length === 0) { return this.img_object.angle(); }
640
641
        if (deg < -270 || deg > 270 || deg % 90 !== 0) { return; }
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
642
        if (!abs) { deg += this.img_object.angle(); }
643
        if (deg < 0) { deg += 360; }
644
        if (deg >= 360) { deg -= 360; }
645
646
        if (deg === this.img_object.angle()) { return; }
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
647
648
        this.img_object.angle(deg);
649
        //the rotate behavior is different in all editors. For now we  just center the
650
        //image. However, it will be better to try to keep the position.
651
        this.center();
652
        this._trigger('angle', 0, { angle: this.img_object.angle() });
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...
653
    },
654
655
    /**
656
    * finds closest multiplier rate for value
657
    * basing on zoom_base and zoom_delta values from settings
658
    * @param Number value zoom value to examine
659
    **/
660
    find_closest_zoom_rate: function(value)
661
    {
662
        if(value == this.options.zoom_base)
663
        {
664
            return 0;
665
        }
666
667
        function div(val1,val2) { return val1 / val2 };
668
        function mul(val1,val2) { return val1 * val2 };
669
670
        var func = (value > this.options.zoom_base)?mul:div;
671
        var sgn = (value > this.options.zoom_base)?1:-1;
672
673
        var mltplr = this.options.zoom_delta;
674
        var rate = 1;
675
676
        while(Math.abs(func(this.options.zoom_base, Math.pow(mltplr,rate)) - value) >
677
              Math.abs(func(this.options.zoom_base, Math.pow(mltplr,rate+1)) - value))
678
        {
679
            rate++;
680
        }
681
682
        return sgn * rate;
683
    },
684
685
    /* update scale info in the container */
686
    update_status: function()
687
    {
688
        if(!this.options.ui_disabled)
689
        {
690
            var percent = Math.round(100*this.img_object.display_height()/this.img_object.orig_height());
691
            if(percent)
692
            {
693
                this.zoom_object.html(percent + "%");
694
            }
695
        }
696
    },
697
698
    /**
699
     * Get some information about the image.
700
     *     Currently orig_(width|height), display_(width|height), angle, zoom and src params are supported.
701
     *
702
     *  @param {string} parameter to check
703
     *  @param {boolean} withoutRotation if param is orig_width or orig_height and this flag is set to true,
704
     *      method will return original image width without considering rotation.
705
     *
706
     */
707
    info: function(param, withoutRotation) {
708
        if (!param) { return; }
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
709
710
        switch (param) {
711
            case 'orig_width':
712
            case 'orig_height':
713
                if (withoutRotation) {
714
                    return (this.img_object.angle() % 180 === 0 ? this.img_object[param]() :
715
                            param === 'orig_width' ? this.img_object.orig_height() :
716
                                                        this.img_object.orig_width());
717
                } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
718
                    return this.img_object[param]();
719
                }
720
            case 'display_width':
721
            case 'display_height':
722
            case 'angle':
723
                return this.img_object[param]();
724
            case 'zoom':
725
                return this.current_zoom;
726
            case 'src':
727
                return this.img_object.object().attr('src');
728
            case 'coords':
729
                return {
730
                    x: this.img_object.x(),
731
                    y: this.img_object.y()
732
                };
733
        }
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
734
    },
735
736
    /**
737
    *   callback for handling mousdown event to start dragging image
738
    **/
739
    _mouseStart: function( e )
740
    {
741
        $.ui.mouse.prototype._mouseStart.call(this, e);
742
        if (this._trigger('onStartDrag', 0, this._getMouseCoords(e)) === false) {
743
            return false;
744
        }
745
746
        /* start drag event*/
747
        this.container.addClass("iviewer_drag_cursor");
748
749
        //#10: fix movement quirks for ipad
750
        this._dragInitialized = !(e.originalEvent.changedTouches && e.originalEvent.changedTouches.length==1);
0 ignored issues
show
Best Practice introduced by
Comparing e.originalEvent.changedTouches.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
751
752
        this.dx = e.pageX - this.img_object.x();
753
        this.dy = e.pageY - this.img_object.y();
754
        return true;
755
    },
756
757
    _mouseCapture: function( e ) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
758
        return true;
759
    },
760
761
    /**
762
     * Handle mouse move if needed. User can avoid using this callback, because
763
     *    he can get the same information through public methods.
764
     *  @param {jQuery.Event} e
765
     */
766
    _handleMouseMove: function(e) {
767
        this._trigger('onMouseMove', e, this._getMouseCoords(e));
768
    },
769
770
    /**
771
    *   callback for handling mousemove event to drag image
772
    **/
773
    _mouseDrag: function(e)
774
    {
775
        $.ui.mouse.prototype._mouseDrag.call(this, e);
776
777
        //#10: imitate mouseStart, because we can get here without it on iPad for some reason
778
        if (!this._dragInitialized) {
779
            this.dx = e.pageX - this.img_object.x();
780
            this.dy = e.pageY - this.img_object.y();
781
            this._dragInitialized = true;
782
        }
783
784
        var ltop =  e.pageY - this.dy;
785
        var lleft = e.pageX - this.dx;
786
787
        this.setCoords(lleft, ltop);
788
        this._trigger('onDrag', e, this._getMouseCoords(e));
789
        return false;
790
    },
791
792
    /**
793
    *   callback for handling stop drag
794
    **/
795
    _mouseStop: function(e)
796
    {
797
        $.ui.mouse.prototype._mouseStop.call(this, e);
798
        this.container.removeClass("iviewer_drag_cursor");
799
        this._trigger('onStopDrag', 0, this._getMouseCoords(e));
800
    },
801
802
    _click: function(e)
803
    {
804
        this._trigger('onClick', 0, this._getMouseCoords(e));
805
    },
806
807
    /**
808
    *   create zoom buttons info box
809
    **/
810
    createui: function()
811
    {
812
        var me=this;
813
814
        $("<div>", { 'class': "iviewer_zoom_in iviewer_common iviewer_button"})
815
                    .bind('mousedown touchstart',function(){me.zoom_by(1); return false;})
816
                    .appendTo(this.container);
817
818
        $("<div>", { 'class': "iviewer_zoom_out iviewer_common iviewer_button"})
819
                    .bind('mousedown touchstart',function(){me.zoom_by(- 1); return false;})
820
                    .appendTo(this.container);
821
822
        $("<div>", { 'class': "iviewer_zoom_zero iviewer_common iviewer_button"})
823
                    .bind('mousedown touchstart',function(){me.set_zoom(100); return false;})
824
                    .appendTo(this.container);
825
826
        $("<div>", { 'class': "iviewer_zoom_fit iviewer_common iviewer_button"})
827
                    .bind('mousedown touchstart',function(){me.fit(this); return false;})
828
                    .appendTo(this.container);
829
830
        this.zoom_object = $("<div>").addClass("iviewer_zoom_status iviewer_common")
831
                                    .appendTo(this.container);
832
833
        $("<div>", { 'class': "iviewer_rotate_left iviewer_common iviewer_button"})
834
                    .bind('mousedown touchstart',function(){me.angle(-90); return false;})
835
                    .appendTo(this.container);
836
837
        $("<div>", { 'class': "iviewer_rotate_right iviewer_common iviewer_button" })
838
                    .bind('mousedown touchstart',function(){me.angle(90); return false;})
839
                    .appendTo(this.container);
840
841
        this.update_status(); //initial status update
842
    }
843
844
} );
845
846
/**
847
 * @class $.ui.iviewer.ImageObject Class represents image and provides public api without
848
 *     extending image prototype.
849
 * @constructor
850
 * @param {boolean} do_anim Do we want to animate image on dimension changes?
851
 */
852
$.ui.iviewer.ImageObject = function(do_anim) {
853
    this._img = $("<img>")
854
            //this is needed, because chromium sets them auto otherwise
855
            .css({ position: "absolute", top :"0px", left: "0px"});
856
857
    this._loaded = false;
858
    this._swapDimensions = false;
859
    this._do_anim = do_anim || false;
860
    this.x(0, true);
861
    this.y(0, true);
862
    this.angle(0);
863
};
864
865
866
/** @lends $.ui.iviewer.ImageObject.prototype */
867
(function() {
868
    /**
869
     * Restore initial object state.
870
     *
871
     * @param {number} w Image width.
872
     * @param {number} h Image height.
873
     */
874
    this._reset = function(w, h) {
875
        this._angle = 0;
876
        this._swapDimensions = false;
877
        this.x(0);
878
        this.y(0);
879
880
        this.orig_width(w);
881
        this.orig_height(h);
882
        this.display_width(w);
883
        this.display_height(h);
884
    };
885
886
    /**
887
     * Check if image is loaded.
888
     *
889
     * @return {boolean}
890
     */
891
    this.loaded = function() { return this._loaded; };
892
893
    /**
894
     * Load image.
895
     *
896
     * @param {string} src Image url.
897
     * @param {Function=} loaded Function will be called on image load.
898
     */
899
    this.load = function(src, loaded, error) {
900
        var self = this;
901
902
        loaded = loaded || jQuery.noop;
903
        this._loaded = false;
904
905
        //If we assign new image url to the this._img IE9 fires onload event and image width and
906
        //height are set to zero. So, we create another image object and load image through it.
907
        var 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...
908
        img.onload = function() {
909
            self._loaded = true;
910
            self._reset(this.width, this.height);
911
912
            self._img
913
                .removeAttr("width")
914
                .removeAttr("height")
915
                .removeAttr("style")
916
                //max-width is reset, because plugin breaks in the twitter bootstrap otherwise
917
                .css({ position: "absolute", top :"0px", left: "0px", maxWidth: "none"})
918
919
            self._img[0].src = src;
920
            loaded();
921
        };
922
923
        img.onerror = error;
924
925
        //we need this because sometimes internet explorer 8 fires onload event
926
        //right after assignment (synchronously)
927
        setTimeout(function() {
928
            img.src = src;
929
        }, 0);
930
931
        this.angle(0);
932
    };
933
934
    this._dimension = function(prefix, name) {
935
        var horiz = '_' + prefix + '_' + name,
936
            vert = '_' + prefix + '_' + (name === 'height' ? 'width' : 'height');
937
        return setter(function(val) {
938
                this[this._swapDimensions ? horiz: vert] = val;
939
            },
940
            function() {
941
                return this[this._swapDimensions ? horiz: vert];
942
            });
943
    };
944
945
    /**
946
     * Getters and setter for common image dimensions.
947
     *    display_ means real image tag dimensions
948
     *    orig_ means physical image dimensions.
949
     *  Note, that dimensions are swapped if image is rotated. It necessary,
950
     *  because as little as possible code should know about rotation.
951
     */
952
    this.display_width = this._dimension('display', 'width'),
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...
953
    this.display_height = this._dimension('display', 'height'),
954
    this.display_diff = function() { return Math.floor( this.display_width() - this.display_height() ) };
955
    this.orig_width = this._dimension('orig', 'width'),
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...
956
    this.orig_height = this._dimension('orig', 'height'),
957
958
    /**
959
     * Setter for  X coordinate. If image is rotated we need to additionaly shift an
960
     *     image to map image coordinate to the visual position.
961
     *
962
     * @param {number} val Coordinate value.
963
     * @param {boolean} skipCss If true, we only set the value and do not touch the dom.
964
     */
965
    this.x = setter(function(val, skipCss) {
966
            this._x = val;
967
            if (!skipCss) {
968
                this._finishAnimation();
969
                this._img.css("left",this._x + (this._swapDimensions ? this.display_diff() / 2 : 0) + "px");
970
            }
971
        },
972
        function() {
973
            return this._x;
974
        });
975
976
    /**
977
     * Setter for  Y coordinate. If image is rotated we need to additionaly shift an
978
     *     image to map image coordinate to the visual position.
979
     *
980
     * @param {number} val Coordinate value.
981
     * @param {boolean} skipCss If true, we only set the value and do not touch the dom.
982
     */
983
    this.y = setter(function(val, skipCss) {
984
            this._y = val;
985
            if (!skipCss) {
986
                this._finishAnimation();
987
                this._img.css("top",this._y - (this._swapDimensions ? this.display_diff() / 2 : 0) + "px");
988
            }
989
        },
990
       function() {
991
            return this._y;
992
       });
993
994
    /**
995
     * Perform image rotation.
996
     *
997
     * @param {number} deg Absolute image angle. The method will work with values 0, 90, 180, 270 degrees.
998
     */
999
    this.angle = setter(function(deg) {
1000
            var prevSwap = this._swapDimensions;
1001
1002
            this._angle = deg;
1003
            this._swapDimensions = deg % 180 !== 0;
1004
1005
            if (prevSwap !== this._swapDimensions) {
1006
                var verticalMod = this._swapDimensions ? -1 : 1;
1007
                this.x(this.x() - verticalMod * this.display_diff() / 2, true);
1008
                this.y(this.y() + verticalMod * this.display_diff() / 2, true);
1009
            };
1010
1011
            var cssVal = 'rotate(' + deg + 'deg)',
1012
                img = this._img;
1013
1014
            jQuery.each(['', '-webkit-', '-moz-', '-o-', '-ms-'], function(i, prefix) {
1015
                img.css(prefix + 'transform', cssVal);
1016
            });
1017
1018
            if (useIeTransforms) {
1019
                jQuery.each(['-ms-', ''], function(i, prefix) {
1020
                    img.css(prefix + 'filter', ieTransforms[deg].filter);
1021
                });
1022
1023
                img.css({
1024
                    marginLeft: ieTransforms[deg].marginLeft * this.display_diff() / 2,
1025
                    marginTop: ieTransforms[deg].marginTop * this.display_diff() / 2
1026
                });
1027
            }
1028
        },
1029
       function() { return this._angle; });
1030
1031
    /**
1032
     * Map point in the container coordinates to the point in image coordinates.
1033
     *     You will get coordinates of point on image with respect to rotation,
1034
     *     but will be set as if image was not rotated.
1035
     *     So, if image was rotated 90 degrees, it's (0,0) point will be on the
1036
     *     top right corner.
1037
     *
1038
     * @param {{x: number, y: number}} point Point in container coordinates.
1039
     * @return  {{x: number, y: number}}
1040
     */
1041
    this.toOriginalCoords = function(point) {
1042
        switch (this.angle()) {
1043
            case 0: return { x: point.x, y: point.y }
1044
            case 90: return { x: point.y, y: this.display_width() - point.x }
1045
            case 180: return { x: this.display_width() - point.x, y: this.display_height() - point.y }
1046
            case 270: return { x: this.display_height() - point.y, y: point.x }
1047
        }
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
1048
    };
1049
1050
    /**
1051
     * Map point in the image coordinates to the point in container coordinates.
1052
     *     You will get coordinates of point on container with respect to rotation.
1053
     *     Note, if image was rotated 90 degrees, it's (0,0) point will be on the
1054
     *     top right corner.
1055
     *
1056
     * @param {{x: number, y: number}} point Point in container coordinates.
1057
     * @return  {{x: number, y: number}}
1058
     */
1059
    this.toRealCoords = function(point) {
1060
        switch (this.angle()) {
1061
            case 0: return { x: this.x() + point.x, y: this.y() + point.y }
1062
            case 90: return { x: this.x() + this.display_width() - point.y, y: this.y() + point.x}
1063
            case 180: return { x: this.x() + this.display_width() - point.x, y: this.y() + this.display_height() - point.y}
1064
            case 270: return { x: this.x() + point.y, y: this.y() + this.display_height() - point.x}
1065
        }
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
1066
    };
1067
1068
    /**
1069
     * @return {jQuery} Return image node. this is needed to add event handlers.
1070
     */
1071
    this.object = setter(jQuery.noop,
1072
                           function() { return this._img; });
1073
1074
    /**
1075
     * Change image properties.
1076
     *
1077
     * @param {number} disp_w Display width;
1078
     * @param {number} disp_h Display height;
1079
     * @param {number} x
1080
     * @param {number} y
1081
     * @param {boolean} skip_animation If true, the animation will be skiped despite the
1082
     *     value set in constructor.
1083
     * @param {Function=} complete Call back will be fired when zoom will be complete.
1084
     */
1085
    this.setImageProps = function(disp_w, disp_h, x, y, skip_animation, complete) {
1086
        complete = complete || jQuery.noop;
1087
1088
        this.display_width(disp_w);
1089
        this.display_height(disp_h);
1090
        this.x(x, true);
1091
        this.y(y, true);
1092
1093
        var w = this._swapDimensions ? disp_h : disp_w;
1094
        var h = this._swapDimensions ? disp_w : disp_h;
1095
1096
        var params = {
1097
            width: w,
1098
            height: h,
1099
            top: y - (this._swapDimensions ? this.display_diff() / 2 : 0) + "px",
1100
            left: x + (this._swapDimensions ? this.display_diff() / 2 : 0) + "px"
1101
        };
1102
1103
        if (useIeTransforms) {
1104
            jQuery.extend(params, {
1105
                marginLeft: ieTransforms[this.angle()].marginLeft * this.display_diff() / 2,
1106
                marginTop: ieTransforms[this.angle()].marginTop * this.display_diff() / 2
1107
            });
1108
        }
1109
1110
        var swapDims = this._swapDimensions,
1111
            img = this._img;
1112
1113
        //here we come: another IE oddness. If image is rotated 90 degrees with a filter, than
1114
        //width and height getters return real width and height of rotated image. The bad news
1115
        //is that to set height you need to set a width and vice versa. Fuck IE.
1116
        //So, in this case we have to animate width and height manually.
1117
        if(useIeTransforms && swapDims) {
1118
            var ieh = this._img.width(),
1119
                iew = this._img.height(),
1120
                iedh = params.height - ieh;
1121
                iedw = params.width - iew;
0 ignored issues
show
Bug introduced by
The variable iedw seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.iedw.
Loading history...
1122
1123
            delete params.width;
1124
            delete params.height;
1125
        }
1126
1127
        if (this._do_anim && !skip_animation) {
1128
            this._img.stop(true)
1129
                .animate(params, {
1130
                    duration: 200,
1131
                    complete: complete,
1132
                    step: function(now, fx) {
1133
                        if(useIeTransforms && swapDims && (fx.prop === 'top')) {
1134
                            var percent = (now - fx.start) / (fx.end - fx.start);
1135
1136
                            img.height(ieh + iedh * percent);
0 ignored issues
show
Bug introduced by
The variable iedh does not seem to be initialized in case useIeTransforms && swapDims on line 1117 is false. Are you sure this can never be the case?
Loading history...
Bug introduced by
The variable ieh does not seem to be initialized in case useIeTransforms && swapDims on line 1117 is false. Are you sure this can never be the case?
Loading history...
1137
                            img.width(iew + iedw * percent);
0 ignored issues
show
Bug introduced by
The variable iew does not seem to be initialized in case useIeTransforms && swapDims on line 1117 is false. Are you sure this can never be the case?
Loading history...
Bug introduced by
The variable iedw does not seem to be initialized in case useIeTransforms && swapDims on line 1117 is false. Are you sure this can never be the case?
Loading history...
1138
                            img.css('top', now);
1139
                        }
1140
                    }
1141
                });
1142
        } else {
1143
            this._img.css(params);
1144
            setTimeout(complete, 0); //both if branches should behave equally.
1145
        }
1146
    };
1147
1148
    //if we set image coordinates we need to be sure that no animation is active atm
1149
    this._finishAnimation = function() {
1150
      this._img.stop(true, true);
1151
    }
1152
1153
}).apply($.ui.iviewer.ImageObject.prototype);
1154
1155
1156
1157
var util = {
1158
    scaleValue: function(value, toZoom)
1159
    {
1160
        return value * toZoom / 100;
1161
    },
1162
1163
    descaleValue: function(value, fromZoom)
1164
    {
1165
        return value * 100 / fromZoom;
1166
    }
1167
};
1168
1169
 } )( jQuery, undefined );
1170