Test Setup Failed
Push — master ( d60e73...a71a5d )
by
unknown
04:37 queued 10s
created

  B

Complexity

Conditions 6
Paths 7

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
nc 7
dl 0
loc 14
rs 8.8571
c 1
b 0
f 0
nop 1
1
/*!
2
 * jQuery Extended Dialog 2.0
3
 *
4
 * Copyright (c) 2013 Oro Inc
5
 * Inspired by DialogExtend Copyright (c) 2010 Shum Ting Hin http://code.google.com/p/jquery-dialogextend/
6
 *
7
 * Licensed under MIT
8
 *   http://www.opensource.org/licenses/mit-license.php
9
 *
10
 * Depends:
11
 *   jQuery 1.7.2
12
 *   jQuery UI Dialog 1.10.2
13
 *
14
 */
15
define(['jquery', 'underscore', 'orotranslation/js/translator', 'oroui/js/tools', 'jquery-ui'], function ($, _, __, tools) {
16
    'use strict';
17
    $.widget( 'ui.dialog', $.ui.dialog, {
18
        version: '2.0.0',
19
20
        _limitToEl: false,
21
22
        _resizeTries: 0,
23
24
        options: $.extend($.ui.dialog.options, {
25
            minimizeTo: false,
26
            maximizedHeightDecreaseBy: false,
27
            allowClose: true,
28
            allowMaximize: false,
29
            allowMinimize: false,
30
            dblclick: false,
31
            titlebar: false,
32
            icons: {
33
                close: 'ui-icon-closethick',
34
                maximize: 'ui-icon-extlink',
35
                minimize: 'ui-icon-minus',
36
                restore: 'ui-icon-newwin'
37
            },
38
            snapshot: null,
39
            state: 'normal',
40
            // Events
41
            beforeCollapse: null,
42
            beforeMaximize: null,
43
            beforeMinimize: null,
44
            beforeRestore: null,
45
            collapse: null,
46
            maximize: null,
47
            minimize: null,
48
            restore: null
49
        }),
50
51
        _allowInteraction: function(e) {
52
            return !!$(e.target).closest('.ui-dialog, .ui-datepicker, .select2-drop, .mce-window, ' +
53
                '.dropdown-menu, .ui-multiselect-menu').length;
54
        },
55
56
        _create: function () {
57
            this._super();
58
59
            this._verifySettings();
60
            this._initBottomLine();
61
62
            this._onBackspacePress = $.proxy(this._onBackspacePress, this);
63
            this._windowResizeHandler = $.proxy(this._windowResizeHandler, this);
64
65
            // prevents history navigation over backspace while dialog is opened
66
            $(document).bind('keydown.dialog', this._onBackspacePress);
67
68
            // Handle window resize
69
            $(window).bind('resize.dialog', this._windowResizeHandler);
70
        },
71
72
        _limitTo: function() {
73
            if (false === this._limitToEl) {
74
                this._limitToEl = this.options.limitTo ? $(this.options.limitTo) : this._appendTo();
75
            }
76
77
            return this._limitToEl;
78
        },
79
80
        _init: function() {
81
            this._super();
82
83
            // Init dialog extended
84
            this._initButtons();
85
            this._initializeContainer();
86
            this._initializeState(this.options.state);
87
        },
88
89
        _destroy: function () {
90
            this._super();
91
92
            // remove custom handler
93
            $(document).unbind('keydown.dialog', this._onBackspacePress);
94
            $(window).unbind('resize.dialog', this._windowResizeHandler);
95
        },
96
97
        _makeDraggable: function() {
98
            this._super();
99
            this.uiDialog.draggable('option', 'containment', this.options.limitTo || 'parent');
100
        },
101
102
        open: function() {
103
            this._super();
104
105
            // @TODO: Remove this fix when Apple fix caret placement bug
106
            this.iOScaretFixer(true);
107
        },
108
109
        close: function() {
110
            $(window).unbind('.dialog');
111
            this._removeMinimizedEl();
112
113
            this._super();
114
115
            // @TODO: Remove this fix when Apple fix caret placement bug
116
            this.iOScaretFixer(false);
117
        },
118
119
        /**
120
         * Fix iOS11 bug when the caret is placed outside the input field
121
         * @TODO: Remove this fix when Apple fix caret placement bug
122
         * @param enabled
123
         */
124
        iOScaretFixer: function(enabled) {
125
            if (_.isMobile() && tools.isIOS()) {
126
                if (enabled) {
127
                    this.scrollTopPosition = $(document).scrollTop();
128
                } else {
129
                    if (this.scrollTopPosition) {
130
                        $(document).scrollTop(this.scrollTopPosition);
131
                    }
132
                }
133
134
                $('body').toggleClass('iosBugFixCaret', enabled)
135
                    .css('top', enabled ? -this.scrollTopPosition : '');
136
            }
137
        },
138
139
        actionsContainer: function() {
140
            return this.uiDialogButtonPane;
141
        },
142
143
        showActionsContainer: function() {
144
            if (!this.uiDialogButtonPane.parent().length) {
145
                this.uiDialog.addClass('ui-dialog-buttons');
146
                this.uiDialogButtonPane.appendTo( this.uiDialog );
147
            }
148
        },
149
150
        state: function () {
151
            return this.options.state;
152
        },
153
154
        minimize: function() {
155
            this._setOption('state', 'minimized');
156
        },
157
158
        maximize: function() {
159
            this._setOption('state', 'maximized');
160
        },
161
162
        collapse: function() {
163
            this._setOption('state', 'collapsed');
164
        },
165
166
        restore: function() {
167
            this._setOption('state', 'normal');
168
        },
169
170
        _minimize: function () {
171
            if (this.state() !== 'minimized') {
172
                this._normalize();
173
            }
174
175
            var widget = this.widget();
176
177
            this._trigger('beforeMinimize');
178
            this._saveSnapshot();
179
            this._setState('minimized');
180
            this._toggleButtons();
181
            this._trigger('minimize');
182
            widget.hide();
183
184
            this._getMinimizeTo().show();
185
186
            // Make copy of widget to disable dialog events
187
            this.minimizedEl = widget.clone();
188
            this.minimizedEl.css({'height': 'auto'});
189
            this.minimizedEl.find('.ui-dialog-content').remove();
190
            this.minimizedEl.find('.ui-resizable-handle').remove();
191
            // Add title attribute to be able to view full window title
192
            var title = this.minimizedEl.find('.ui-dialog-title');
193
            title.disableSelection().attr('title', title.text());
194
            var self = this;
195
            this.minimizedEl.find('.ui-dialog-titlebar').dblclick(function() {
196
                self.uiDialogTitlebar.dblclick();
197
            });
198
            // Proxy events to original window
199
            var buttons = ['close', 'maximize', 'restore'];
200
            for (var i = 0; i < buttons.length; i++) {
201
                var btnClass = '.ui-dialog-titlebar-' + buttons[i];
202
                this.minimizedEl.find(btnClass).click(
203
                    function(btnClass) {
204
                        return function() {
205
                            widget.find(btnClass).click();
206
                            return false;
207
                        }
208
                    }(btnClass));
209
            }
210
            this.minimizedEl.show();
211
            this.minimizedEl.appendTo(this._getMinimizeTo());
212
213
            return this;
214
        },
215
216
        _collapse: function () {
217
            var newHeight = this._getTitleBarHeight();
218
219
            this._trigger('beforeCollapse');
220
            this._saveSnapshot();
221
            // modify dialog size (after hiding content)
222
            this._setOptions({
223
                resizable: false,
224
                height: newHeight,
225
                maxHeight: newHeight
226
            });
227
            // mark new state
228
            this._setState('collapsed');
229
            // trigger custom event
230
            this._trigger('collapse');
231
232
            return this;
233
        },
234
235
        _maximize: function () {
236
            if (this.state() !== 'maximized') {
237
                this._normalize();
238
            }
239
240
            this._trigger('beforeMaximize');
241
            this._saveSnapshot();
242
            this._calculateNewMaximizedDimensions($.proxy(function() {
243
                this._setState('maximized');
244
                this._toggleButtons();
245
                this._trigger('maximize');
246
            }, this));
247
248
            return this;
249
        },
250
251
        _restore: function () {
252
            this._trigger('beforeRestore');
253
            // restore to normal
254
            this._restoreWithoutTriggerEvent();
255
            this._setState('normal');
256
            this._toggleButtons();
257
            this._trigger('restore');
258
259
            return this;
260
        },
261
262
        _normalize: function() {
263
            if (this.state() !== 'normal') {
264
                this.disableStateChangeTrigger = true;
265
                this._setOption('state', 'normal');
266
                this.disableStateChangeTrigger = false;
267
            }
268
        },
269
270
        _initBottomLine: function() {
271
            this.bottomLine = $('#dialog-extend-parent-bottom');
272
            if (!this.bottomLine.length) {
273
                this.bottomLine = $('<div id="dialog-extend-parent-bottom"></div>');
274
                this.bottomLine.css({
275
                    position: 'fixed',
276
                    bottom: 0,
277
                    left: 0
278
                })
279
                .appendTo(document.body);
280
            }
281
            return this;
282
        },
283
284
        _initializeMinimizeContainer: function() {
285
            this.options.minimizeTo = $('#dialog-extend-fixed-container');
286
            if (!this.options.minimizeTo.length) {
287
                this.options.minimizeTo = $('<div id="dialog-extend-fixed-container"></div>');
288
                this.options.minimizeTo.addClass('ui-dialog-minimize-container');
289
                this.options.minimizeTo
290
                .css({
291
                    position: 'fixed',
292
                    bottom: 1,
293
                    left: this._limitTo().offset().left,
294
                    zIndex: 9999
295
                })
296
                .hide()
297
                .appendTo(this._appendTo());
298
            }
299
        },
300
301
        _getMinimizeTo: function() {
302
            if (this.options.minimizeTo === false) {
303
                this._initializeMinimizeContainer();
304
            }
305
            return $(this.options.minimizeTo);
306
        },
307
308
        _calculateNewMaximizedDimensions: function(onResizeCallback) {
309
            if (this._limitTo().is(':visible')) {
310
                this._resizeTries = 0;
311
                var newHeight = this._getContainerHeight();
312
                var newWidth = this._limitTo().width();
313
                this._setOptions({
314
                    resizable: false,
315
                    draggable : false,
316
                    height: newHeight,
317
                    width: newWidth,
318
                    position: {
319
                        my: 'left top',
320
                        at: 'left top',
321
                        of: this._limitTo()
322
                    }
323
                });
324
                this.widget().css('position', 'fixed'); // remove scroll when maximized
325
                if ($.isFunction(onResizeCallback)) {
326
                    onResizeCallback();
327
                }
328
            } else {
329
                this._resizeTries++;
330
                if (this._resizeTries < 100) {
331
                    setTimeout($.proxy(function() {this._calculateNewMaximizedDimensions(onResizeCallback);}, this), 500);
332
                } else {
333
                    this._resizeTries = 0;
334
                }
335
            }
336
337
            return this;
338
        },
339
340
        _size: function() {
341
            var cssProperties = _.pick(this.options, ['width', 'height', 'maxWidth', 'minWidth']);
342
            this.uiDialog.css(cssProperties);
343
            if ( this.uiDialog.is( ':data(ui-resizable)' ) ) {
344
                this.uiDialog.resizable( 'option', 'minHeight', this._minHeight() );
345
            }
346
        },
347
348
        _moveToVisible: function() {
349
            var $widget = this.widget();
350
            if ($widget.length > 0) {
351
                var offset = $widget.offset();
352
                this._setOptions({
353
                    position: [offset.left, offset.top]
354
                });
355
            }
356
            return this;
357
        },
358
359
        _getTitleBarHeight: function() {
360
            return this.uiDialogTitlebar.height() + 15;
361
        },
362
363
        _getContainerHeight: function() {
364
            var heightDelta = 0;
365
            if (this.options.maximizedHeightDecreaseBy) {
366
                if ($.isNumeric(this.options.maximizedHeightDecreaseBy)) {
367
                    heightDelta = this.options.maximizedHeightDecreaseBy;
368
                } else if (this.options.maximizedHeightDecreaseBy === 'minimize-bar') {
369
                    heightDelta = this._getMinimizeTo().height();
370
                } else {
371
                    heightDelta = $(this.maximizedHeightDecreaseBy).height();
372
                }
373
            }
374
375
            // Maximize window to container, or to viewport in case when container is higher
376
            var baseHeight = this._limitTo().height();
377
            var visibleHeight = this.bottomLine.offset().top - this._limitTo().offset().top;
378
            var currentHeight = baseHeight > visibleHeight ? visibleHeight : baseHeight;
379
            return currentHeight - heightDelta;
380
        },
381
382
        _initButtons: function (el) {
383
            var self = this;
384
            if (typeof el === 'undefined') {
385
                el = this;
0 ignored issues
show
Unused Code introduced by
The assignment to variable el seems to be never used. Consider removing it.
Loading history...
386
            }
387
            // start operation on titlebar
388
            // create container for buttons
389
            var buttonPane = $('<div class="ui-dialog-titlebar-buttonpane"></div>').appendTo(this.uiDialogTitlebar);
390
            // move 'close' button to button-pane
391
            this._buttons = {};
392
            this.uiDialogTitlebarClose
393
            // override some unwanted jquery-ui styles
394
            .css({ 'position': 'static', 'top': 'auto', 'right': 'auto', 'margin': 0 })
395
            .attr('title', __('close'))
396
            // change icon
397
            .find('.ui-icon').removeClass('ui-icon-closethick').addClass(this.options.icons.close).end()
398
            // move to button-pane
399
            .appendTo(buttonPane)
400
            .end();
401
            // append other buttons to button-pane
402
            var types =  ['maximize', 'restore', 'minimize'];
403
            for (var key in types) {
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...
404
                if (typeof types[key] === 'string') {
405
                    var type = types[key];
406
                    var button = this.options.icons[type];
407
                    if (typeof this.options.icons[type] === 'string') {
408
                        button = '<a class="ui-dialog-titlebar-' + type + ' ui-corner-all" href="#" title="' + __(type)+ '"><span class="ui-icon ' + this.options.icons[type] + '">' + type + '</span></a>';
409
410
                    } else {
411
                        button.addClass('ui-dialog-titlebar-' + type);
412
                    }
413
                    button = $(button);
414
                    button
415
                    .attr('role', 'button')
416
                    .mouseover(function() {
417
                        $(this).addClass('ui-state-hover');
418
                    })
419
                    .mouseout(function() {
420
                        $(this).removeClass('ui-state-hover');
421
                    })
422
                    .focus(function() {
423
                        $(this).addClass('ui-state-focus');
424
                    })
425
                    .blur(function() {
426
                        $(this).removeClass('ui-state-focus');
427
                    });
428
                    this._buttons[type] = button;
429
                    buttonPane.append(button);
430
                }
431
            }
432
433
            this.uiDialogTitlebarClose.toggle(this.options.allowClose);
434
435
            this._buttons.maximize
436
            .toggle(this.options.allowMaximize)
437
            .click(function (e) {
438
                e.preventDefault();
439
                self.maximize();
440
            });
441
442
            this._buttons.minimize
443
            .toggle(this.options.allowMinimize)
444
            .click(function (e) {
445
                e.preventDefault();
446
                self.minimize();
447
            });
448
449
            this._buttons.restore
450
            .hide()
451
            .click(function (e) {
452
                e.preventDefault();
453
                self.restore();
454
            });
455
456
            // other titlebar behaviors
457
            this.uiDialogTitlebar
458
            // on-dblclick-titlebar : maximize/minimize/collapse/restore
459
            .dblclick(function (evt) {
0 ignored issues
show
Unused Code introduced by
The parameter evt 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...
460
                if (self.options.dblclick && self.options.dblclick.length) {
461
                    if (self.state() !== 'normal') {
462
                        self.restore();
463
                    } else {
464
                        self[self.options.dblclick]();
465
                    }
466
                }
467
            })
468
            // avoid text-highlight when double-click
469
            .select(function () {
470
                return false;
471
            });
472
473
            return this;
474
        },
475
476
        _windowResizeHandler: function(e) {
477
            if (e.target === window) {
478
                switch (this.state()) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
479
                    case 'maximized':
480
                        this._calculateNewMaximizedDimensions();
481
                        break;
482
                    case 'normal':
483
                        this._moveToVisible();
484
                        break;
485
                }
486
            }
487
        },
488
489
        _onBackspacePress: function (e) {
490
            // prevents history navigation over backspace while dialog is opened
491
            var exclude = ':button,:reset,:submit,:checkbox,:radio,select,[type=image],[type=file]';
492
            if (this._isOpen && e.keyCode === 8 && !$(e.target).not(exclude).is(':input, [contenteditable]')) {
493
                e.preventDefault();
494
            }
495
        },
496
497
        _createTitlebar: function () {
498
            this._super();
499
            this.uiDialogTitlebar.disableSelection();
500
501
            // modify title bar
502
            switch (this.options.titlebar) {
503
                case false:
504
                    // do nothing
505
                    break;
506
                case 'transparent':
507
                    // remove title style
508
                    this.uiDialogTitlebar
509
                    .css({
510
                        'background-color': 'transparent',
511
                        'background-image': 'none',
512
                        'border': 0
513
                    });
514
                    break;
515
                default:
516
                    $.error('jQuery.dialogExtend Error : Invalid <titlebar> value "' + this.options.titlebar + '"');
517
            }
518
519
            return this;
520
        },
521
522
        _restoreFromNormal: function() {
523
            return this;
524
        },
525
526
        _restoreFromCollapsed: function () {
527
            var original = this._loadSnapshot();
528
            // restore dialog
529
            this._setOptions({
530
                resizable: original.config.resizable,
531
                height: original.size.height - this._getTitleBarHeight(),
532
                maxHeight: original.size.maxHeight
533
            });
534
535
            return this;
536
        },
537
538
        _restoreFromMaximized: function () {
539
            var original = this._loadSnapshot(),
540
                widget = this.widget().get(0),
541
                widgetCSS = {
542
                    'min-height': widget.style.minHeight,
543
                    'border': widget.style.border,
544
                    'position': 'fixed',
545
                    'left': this._getVisibleLeft(original.position.left, original.size.width),
546
                    'top': this._getVisibleTop(original.position.top, original.size.height)
547
                };
548
            // reset css props of widget to correct calculation non-content height in jquery-ui code
549
            this.widget().css({'min-height': '0', 'border': '0 none'});
550
551
            // restore dialog
552
            this._setOptions({
553
                resizable: original.config.resizable,
554
                draggable: original.config.draggable,
555
                height: original.size.height,
556
                width: original.size.width,
557
                maxHeight: original.size.maxHeight,
558
                position: [ original.position.left, original.position.top ]
559
            });
560
561
            // adjust widget position
562
            this.widget().css(widgetCSS);
563
564
            return this;
565
        },
566
567
        _restoreFromMinimized: function () {
568
            this._removeMinimizedEl();
569
            this.widget().show();
570
571
            var original = this._loadSnapshot();
572
573
            // Calculate position to be visible after maximize
574
            this.widget().css({
575
                position: 'fixed',
576
                left: this._getVisibleLeft(original.position.left, original.size.width),
577
                top: this._getVisibleTop(original.position.top, original.size.height)
578
            });
579
580
            return this;
581
        },
582
583
        _removeMinimizedEl: function() {
584
            if (this.minimizedEl) {
585
                this.minimizedEl.remove();
586
            }
587
        },
588
589
        _getVisibleLeft: function(left, width) {
590
            var containerWidth = this._limitTo().width();
591
            if (left + width > containerWidth) {
592
                return containerWidth - width;
593
            }
594
            return left;
595
        },
596
597
        _getVisibleTop: function(top, height) {
598
            var visibleTop = this.bottomLine.offset().top;
599
            if (top + height > visibleTop) {
600
                return visibleTop - height;
601
            }
602
            return top;
603
        },
604
605
        _restoreWithoutTriggerEvent: function () {
606
            var beforeState = this.state();
607
            var method = '_restoreFrom' + beforeState.charAt(0).toUpperCase() + beforeState.slice(1);
608
            if ($.isFunction(this[method])) {
609
                this[method]();
610
            } else {
611
                $.error('jQuery.dialogExtend Error : Cannot restore dialog from unknown state "' + beforeState + '"');
612
            }
613
614
            return this;
615
        },
616
617
        _saveSnapshot: function () {
618
            // remember all configs under normal state
619
            if (this.state() === 'normal') {
620
                this._setOption('snapshot', this.snapshot());
621
            }
622
623
            return this;
624
        },
625
626
        snapshot: function() {
627
            return {
628
                config: {
629
                    resizable: this.options.resizable,
630
                    draggable: this.options.draggable
631
                },
632
                size: {
633
                    height: this.widget().height(),
634
                    width: this.options.width,
635
                    maxHeight: this.options.maxHeight
636
                },
637
                'position': this.widget().offset()
638
            };
639
        },
640
641
        _loadSnapshot: function() {
642
            return this.options.snapshot;
643
        },
644
645
        _setOption: function(key, value) {
646
            if (key === 'state') {
647
                this._initializeState(value);
648
            }
649
650
            this._superApply(arguments);
651
652
            if (key === 'appendTo') {
653
                this._initializeContainer();
654
            }
655
        },
656
657
        _initializeState: function(state) {
658
            if (!this.widget().hasClass('ui-dialog-' + state)) {
659
                switch (state) {
660
                    case 'maximized':
661
                        this._maximize();
662
                        break;
663
                    case 'minimized':
664
                        this._minimize();
665
                        break;
666
                    case 'collapsed':
667
                        this._collapse();
668
                        break;
669
                    default:
670
                        this._restore();
671
                }
672
            }
673
        },
674
675
        _initializeContainer: function() {
676
            // Fix parent position
677
            var appendTo = this._appendTo();
678
            if (appendTo.css('position') === 'static') {
679
                appendTo.css('position', 'relative');
680
            }
681
        },
682
683
        _setState: function (state) {
684
            var oldState = this.options.state;
685
            this.options.state = state;
686
            // toggle data state
687
            this.widget()
688
            .removeClass('ui-dialog-normal ui-dialog-maximized ui-dialog-minimized ui-dialog-collapsed')
689
            .addClass('ui-dialog-' + state);
690
691
            // Trigger state change event
692
            if (!this.disableStateChangeTrigger) {
693
                var snapshot = this._loadSnapshot();
694
                if (!snapshot && this.state() === 'normal') {
695
                    snapshot = this.snapshot();
696
                }
697
                this._trigger('stateChange', null, {
698
                    state: this.state(),
699
                    oldState: oldState,
700
                    snapshot: snapshot
701
                });
702
            }
703
704
            return this;
705
        },
706
707
        _toggleButtons: function () {
708
            // show or hide buttons & decide position
709
            this._buttons.maximize
710
            .toggle(this.state() !== 'maximized' && this.options.allowMaximize);
711
712
            this._buttons.minimize
713
            .toggle(this.state() !== 'minimized' && this.options.allowMinimize);
714
715
            this._buttons.restore
716
            .toggle(this.state() !== 'normal' && ( this.options.allowMaximize || this.options.allowMinimize ))
717
            .css({ 'right': this.state() === 'maximized' ? '1.4em' : this.state() === 'minimized' ? !this.options.allowMaximize ? '1.4em' : '2.5em' : '-9999em' });
718
719
            return this;
720
        },
721
722
        _verifySettings: function () {
723
            var self = this;
724
            var checkOption = function(option, options) {
725
                if (self.options[option] && options.indexOf(self.options[option]) === -1) {
726
                    $.error('jQuery.dialogExtend Error : Invalid <' + option + '> value "' + self.options[option] + '"');
727
                    self.options[option] = false;
728
                }
729
            };
730
731
            checkOption('dblclick', ['maximize', 'minimize', 'collapse']);
732
            checkOption('titlebar', ['transparent']);
733
734
            return this;
735
        }
736
    });
737
});
738