PageSourceSearch

https://www.dupage88.net/site/javax.faces.resource/components.js.h…?ln=primefaces&v=8.0.12

js dupage88.net collected 2026-09-24 12:29:14 UTC 835,146 bytes, 24,675 lines download raw bytes

1if (!PrimeFaces.dialog) {
2
3    PrimeFaces.dialog = {};
4
5    PrimeFaces.dialog.DialogHandler = {
6
7        openDialog: function(cfg) {
8            var rootWindow = this.findRootWindow(),
9            dialogId = cfg.sourceComponentId + '_dlg';
10
11            if(rootWindow.document.getElementById(dialogId)) {
12                return;
13            }
14
15            // The widget that opens a dialog can be nested inside of a frame which might be nested again.
16            // The dialog is put in the outermost frame to be able to fill the whole browser tab,
17            // so we traverse upwards to find the root window and put the dialog DOM in there.
18            // When a dialog is closed, we need to clean up the global variables and notify the source widget for the dialog return feature.
19            // Accessing a component nested within frames requires recursive resolving of frames.
20            // Every frame has it's own contentWindow and thus also it's own document object.
21            // To be able to access a DOM element from an outer frame, one needs to first resolve the containing frame,
22            // and then resolve the element from the contentWindow. With nested frames, nested frame resolving has to be done.
23            // In order to do this, we traverse up the window frameElement until we reach the top window.
24            // While traversing up, we construct a selector for finding the frameElement from within the parent window.
25            // We build up the selectors backwards as we traverse up. Imagine the example
26            //
27            // --------------------------------------------------
28            // | Frame 1                                        |
29            // |           -------------------------------      |
30            // |           | Frame 1_1                   |      |
31            // |           |                             |      |
32            // |           |  ------------               |      |
33            // |           |  | Button 1 |               |      |
34            // |           |  ------------               |      |
35            // |           |                             |      |
36            // |           -------------------------------      |
37            // |------------------------------------------------|
38            // | Frame 2                                        |
39            // |                                                |
40            // |                                                |
41            // |                                                |
42            // --------------------------------------------------
43            //
44            // Here "Button 1" is our source widget that opened the dialog.
45            // The root window contains two frames "Frame 1" and "Frame 2".
46            // The "Frame 1" contains another frame "Frame 1_1" within which the widget lives.
47            // Since we have to install the dialog in the root window, we need to be able to get access
48            // to the source widget when closing the dialog.
49            // The only way to find the DOM node, is by traversing into "Frame 1" then into "Frame 1_1" and look it up there.
50            // So from the root window we do e.g. `$(rootWindow.document).find("#frame1").contentWindow` to get into "Frame 1".
51            // We do the same to get into "Frame 1_1" e.g. `$(frame1Window.document).find("#frame1_1").contentWindow`.
52            // Finally, we can look up the source widget `$(frame1_1Window.document).find("#sourceWidgetId")`.
53
54            var sourceFrames = function() {
55                var w = window;
56                var sourceFrames = [];
57                // Traverse up frameElement i.e. while we are in frames
58                while(w.frameElement) {
59                    var parent = w.parent;
60                    if (parent.PF === undefined) {
61                        break;
62                    }
63
64                    // Since we traverse DOM elements upwards, we build the selector backwards i.e. from target to source.
65                    // This is why we use `unshift` which is like an `addAtIndex(0, object)`.
66                    // If an element has an id, we can use that to uniquely identify the DOM element and can jump to the next parent window.
67                    // If we can't find an id, we collect class names and the tag name of an element.
68                    // If that doesn't uniquely identify an element within it's parent, we also append the node index via the `:eq(index)` selector.
69                    // We connect selectors for each DOM element with the `>` operator.
70                    var e = w.frameElement;
71                    var pieces = [];
72
73                    // Traverse up tags from the frameElement to generate an identifying selector
74                    for (; e && e.tagName !== undefined; e = e.parentNode) {
75                        if (e.id && !/\s/.test(e.id)) {
76                            // If we find a parent with an id, we can use that as basis and stop there
77                            pieces.unshift(e.id);
78                            pieces.unshift('#');
79                            pieces.unshift(' > ');
80                            break;
81                        } else if (e.className) {
82                            // Without an id, we try to use a combination of :eq, class names and tag name and hope a parent has an id
83                            var classes = e.className.split(' ');
84                            var classSelectorPieces = [];
85                            for (var i in classes) {
86                                if (classes.hasOwnProperty(i) && classes[i]) {
87                                    classSelectorPieces.unshift(classes[i]);
88                                    classSelectorPieces.unshift('.');
89                                }
90                            }
91                            classSelectorPieces.unshift(e.tagName);
92
93                            var classSelector = classSelectorPieces.join('');
94                            var elems = $(e.parentNode).find(classSelector);
95                            if (elems.length > 1) {
96                                pieces.unshift(":eq(" + elems.index(e) + ")");
97                            }
98                            pieces.unshift(classSelector);
99                        } else {
100                            // Without classes, we try to work with :eq and the tag name
101                            var elems = $(e.parentNode).find(e.tagName);
102                            if (elems.length > 1) {
103                                pieces.unshift(":eq(" + elems.index(e) + ")");
104                            }
105                            pieces.unshift(e.tagName);
106                        }
107                        pieces.unshift(' > ');
108                    }
109
110                    var s = pieces.slice(1).join('');
111
112                    sourceFrames.unshift(s);
113                    w = parent;
114                };
115
116                return sourceFrames;
117            }();
118
119            var dialogWidgetVar = cfg.options.widgetVar;
120            if (!dialogWidgetVar) {
121                dialogWidgetVar = cfg.sourceComponentId.replace(/:/g, '_') + '_dlgwidget';
122            }
123
124            var styleClass = cfg.options.styleClass||'',
125            dialogDOM = $('<div id="' + dialogId + '" class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-shadow ui-hidden-container ui-overlay-hidden ' + styleClass + '"' +
126                    ' data-pfdlgcid="' + PrimeFaces.escapeHTML(cfg.pfdlgcid) + '" data-widget="' + dialogWidgetVar + '"></div>')
127                    .append('<div class="ui-dialog-titlebar ui-widget-header ui-helper-clearfix ui-corner-top"><span id="' + dialogId + '_title" class="ui-dialog-title"></span></div>');
128
129            var titlebar = dialogDOM.children('.ui-dialog-titlebar');
130            if(cfg.options.closable !== false) {
131                titlebar.append('<a class="ui-dialog-titlebar-icon ui-dialog-titlebar-close ui-corner-all" href="#" role="button"><span class="ui-icon ui-icon-closethick"></span></a>');
132            }
133
134            if(cfg.options.minimizable) {
135                titlebar.append('<a class="ui-dialog-titlebar-icon ui-dialog-titlebar-minimize ui-corner-all" href="#" role="button"><span class="ui-icon ui-icon-minus"></span></a>');
136            }
137
138            if(cfg.options.maximizable) {
139                titlebar.append('<a class="ui-dialog-titlebar-icon ui-dialog-titlebar-maximize ui-corner-all" href="#" role="button"><span class="ui-icon ui-icon-extlink"></span></a>');
140            }
141
142            dialogDOM.append('<div class="ui-dialog-content ui-widget-content ui-df-content" style="height: auto;">' +
143                    '<iframe style="border:0 none" frameborder="0"></iframe>' +
144                    '</div>');
145
146            dialogDOM.appendTo(rootWindow.document.body);
147
148            var dialogFrame = dialogDOM.find('iframe'),
149            symbol = cfg.url.indexOf('?') === -1 ? '?' : '&',
150            frameURL = cfg.url.indexOf('pfdlgcid') === -1 ? cfg.url + symbol + 'pfdlgcid=' + cfg.pfdlgcid: cfg.url,
151            frameWidth = cfg.options.contentWidth||640;
152
153            dialogFrame.width(frameWidth);
154
155            if(cfg.options.iframeTitle) {
156               dialogFrame.attr('title', cfg.options.iframeTitle);
157            }
158
159            dialogFrame.on('load', function() {
160                var $frame = $(this),
161                headerElement = $frame.contents().find('title'),
162                isCustomHeader = false;
163
164                if(cfg.options.headerElement) {
165                    var customHeaderId = PrimeFaces.escapeClientId(cfg.options.headerElement),
166                    customHeaderElement = dialogFrame.contents().find(customHeaderId);
167
168                    if(customHeaderElement.length) {
169                        headerElement = customHeaderElement;
170                        isCustomHeader = true;
171                    }
172                }
173
174                if(!$frame.data('initialized')) {
175                    PrimeFaces.cw.call(rootWindow.PrimeFaces, 'DynamicDialog', dialogWidgetVar, {
176                        id: dialogId,
177                        position: cfg.options.position||'center',
178                        sourceFrames: sourceFrames,
179                        sourceComponentId: cfg.sourceComponentId,
180                        sourceWidgetVar: cfg.sourceWidgetVar,
181                        onHide: function() {
182                            var $dialogWidget = this,
183                            dialogFrame = this.content.children('iframe');
184
185                            if(dialogFrame.get(0).contentWindow.PrimeFaces) {
186                                this.destroyIntervalId = setInterval(function() {
187                                    if(dialogFrame.get(0).contentWindow.PrimeFaces.ajax.Queue.isEmpty()) {
188                                        clearInterval($dialogWidget.destroyIntervalId);
189                                        dialogFrame.attr('src','about:blank');
190                                        $dialogWidget.jq.remove();
191                                    }
192                                }, 10);
193                            }
194                            else {
195                                dialogFrame.attr('src','about:blank');
196                                $dialogWidget.jq.remove();
197                            }
198
199                            rootWindow.PrimeFaces.widgets[dialogWidgetVar] = undefined;
200                        },
201                        modal: cfg.options.modal,
202                        blockScroll: cfg.options.blockScroll,
203                        resizable: cfg.options.resizable,
204                        hasIframe: true,
205                        draggable: cfg.options.draggable,
206                        width: cfg.options.width,
207                        height: cfg.options.height,
208                        minimizable: cfg.options.minimizable,
209                        maximizable: cfg.options.maximizable,
210                        headerElement: cfg.options.headerElement,
211                        responsive: cfg.options.responsive,
212                        closeOnEscape: cfg.options.closeOnEscape,
213                        focus: cfg.options.focus
214                    });
215                }
216
217                var title = rootWindow.PF(dialogWidgetVar).titlebar.children('span.ui-dialog-title');
218                if(headerElement.length > 0) {
219                    if(isCustomHeader) {
220                        title.append(headerElement);
221                        headerElement.show();
222                    }
223                    else {
224                        title.text(headerElement.text());
225                    }
226
227                    dialogFrame.attr('title', title.text());
228                }
229
230                //adjust height
231                var frameHeight = null;
232                if(cfg.options.contentHeight)
233                    frameHeight = cfg.options.contentHeight;
234                else
235                    frameHeight = $frame.get(0).contentWindow.document.body.scrollHeight + (PrimeFaces.env.browser.webkit ? 5 : 25);
236
237                $frame.css('height', frameHeight);
238
239                // fix #1290 - dialogs are not centered vertically
240                dialogFrame.data('initialized', true);
241                rootWindow.PF(dialogWidgetVar).show();
242            })
243            .attr('src', frameURL);
244        },
245
246        closeDialog: function(cfg) {
247            var rootWindow = this.findRootWindow(),
248            dlgs = $(rootWindow.document.body).children('div.ui-dialog[data-pfdlgcid="' + $.escapeSelector(cfg.pfdlgcid) +'"]').not('[data-queuedforremoval]'),
249            dlgsLength = dlgs.length,
250            dlg = dlgs.eq(dlgsLength - 1),
251            parentDlg = dlgsLength > 1 ? dlgs.eq(dlgsLength - 2) : null,
252            dlgWidget = rootWindow.PF(dlg.data('widget')),
253            sourceWidgetVar = dlgWidget.cfg.sourceWidgetVar,
254            sourceComponentId = dlgWidget.cfg.sourceComponentId,
255            dialogReturnBehavior = null,
256            windowContext = null;
257
258            dlg.attr('data-queuedforremoval', true);
259
260            if(parentDlg) {
261                var parentDlgFrame = parentDlg.find('> .ui-dialog-content > iframe').get(0),
262                windowContext = parentDlgFrame.contentWindow||parentDlgFrame;
263                sourceWidget = windowContext.PF(sourceWidgetVar);
264            }
265            else {
266                // We have to resolve the frames from the root window to the source widget to invoke the dialog return behavior
267                // Each source frame element is a selector. We step into every nested frame until we are in the source widget frame.
268                windowContext = rootWindow;
269                var frames = dlgWidget.cfg.sourceFrames;
270                for (var i = 0; i < frames.length; i++) {
271                    windowContext = $(windowContext.document).find(frames[i]).get(0).contentWindow;
272                }
273            }
274
275            if(sourceWidgetVar) {
276                var sourceWidget = windowContext.PF(sourceWidgetVar);
277                dialogReturnBehavior = sourceWidget.cfg.behaviors ? sourceWidget.cfg.behaviors['dialogReturn']: null;
278            }
279            else if(sourceComponentId) {
280                var dialogReturnBehaviorStr = $(windowContext.document.getElementById(sourceComponentId)).data('dialogreturn');
281                if(dialogReturnBehaviorStr) {
282                    dialogReturnBehavior = windowContext.eval('(function(ext){this.' + dialogReturnBehaviorStr + '})');
283                }
284            }
285
286            if(dialogReturnBehavior) {
287                var ext = {
288                        params: [
289                            {name: sourceComponentId + '_pfdlgcid', value: cfg.pfdlgcid}
290                        ]
291                    };
292
293                dialogReturnBehavior.call(windowContext, ext);
294            }
295
296            dlgWidget.hide();
297        },
298
299        showMessageInDialog: function(msg) {
300            if(!this.messageDialog) {
301                var messageDialogDOM = $('<div id="primefacesmessagedlg" class="ui-message-dialog ui-dialog ui-widget ui-widget-content ui-corner-all ui-shadow ui-hidden-container"></div>')
302                            .append('<div class="ui-dialog-titlebar ui-widget-header ui-helper-clearfix ui-corner-top"><span class="ui-dialog-title"></span>' +
303                            '<a class="ui-dialog-titlebar-icon ui-dialog-titlebar-close ui-corner-all" href="#" role="button"><span class="ui-icon ui-icon-closethick"></span></a></div>' +
304                            '<div class="ui-dialog-content ui-widget-content" style="height: auto;"></div>')
305                            .appendTo(document.body);
306
307                PrimeFaces.cw('Dialog', 'primefacesmessagedialog', {
308                    id: 'primefacesmessagedlg',
309                    modal:true,
310                    draggable: false,
311                    resizable: false,
312                    showEffect: 'fade',
313                    hideEffect: 'fade'
314                });
315                this.messageDialog = PF('primefacesmessagedialog');
316                this.messageDialog.titleContainer = this.messageDialog.titlebar.children('span.ui-dialog-title');
317            }
318
319            var escape = msg.escape !== false;
320            var summaryHtml = msg.summary ? msg.summary.split(/\r\n|\n|\r/g).map(function(line) { return escape ? PrimeFaces.escapeHTML(line) : line; }).join("<br>") : "";
321            this.messageDialog.titleContainer.html(summaryHtml);
322
323            var detailHtml = msg.detail ? msg.detail.split(/\r\n|\n|\r/g).map(function(line) { return escape ? PrimeFaces.escapeHTML(line) : line; }).join("<br>") : "";
324            this.messageDialog.content.html('').append('<span class="ui-dialog-message ui-messages-' + msg.severity.split(' ')[0].toLowerCase() + '-icon"></span>').append(detailHtml);
325            this.messageDialog.show();
326        },
327
328        confirm: function(msg) {
329            if(PrimeFaces.confirmDialog) {
330                PrimeFaces.confirmSource = (typeof(msg.source) === 'string') ? $(PrimeFaces.escapeClientId(msg.source)) : $(msg.source);
331                PrimeFaces.confirmDialog.showMessage(msg);
332            }
333            else {
334                PrimeFaces.warn('No global confirmation dialog available.');
335            }
336        },
337
338        findRootWindow: function() {
339            // Note that the determination of the sourceFrames is tightly coupled to the same traversing logic, so keep both in sync
340            var w = window;
341            while(w.frameElement) {
342                var parent = w.parent;
343                if (parent.PF === undefined) {
344                	break;
345                }
346                w = parent;
347            };
348
349            return w;
350        }
351    };
352}
353
354  /**
355 * PrimeFaces Accordion Panel Widget
356 */
357PrimeFaces.widget.AccordionPanel = PrimeFaces.widget.BaseWidget.extend({
358
359    init: function(cfg) {
360        this._super(cfg);
361
362        this.stateHolder = $(this.jqId + '_active');
363        this.headers = this.jq.children('.ui-accordion-header');
364        this.panels = this.jq.children('.ui-accordion-content');
365        this.cfg.rtl = this.jq.hasClass('ui-accordion-rtl');
366        this.cfg.expandedIcon = 'ui-icon-triangle-1-s';
367        this.cfg.collapsedIcon = this.cfg.rtl ? 'ui-icon-triangle-1-w' : 'ui-icon-triangle-1-e';
368
369        this.initActive();
370        this.bindEvents();
371
372        if(this.cfg.dynamic && this.cfg.cache) {
373            this.markLoadedPanels();
374        }
375    },
376
377    initActive: function() {
378        if(this.cfg.multiple) {
379            this.cfg.active = [];
380
381            if (this.stateHolder.val().length > 0) {
382                var indexes = this.stateHolder.val().split(',');
383                for(var i = 0; i < indexes.length; i++) {
384                    this.cfg.active.push(parseInt(indexes[i]));
385                }
386            }
387        }
388        else {
389            this.cfg.active = parseInt(this.stateHolder.val());
390        }
391    },
392
393    bindEvents: function() {
394        var $this = this;
395
396        this.headers.mouseover(function() {
397            var element = $(this);
398            if(!element.hasClass('ui-state-active')&&!element.hasClass('ui-state-disabled')) {
399                element.addClass('ui-state-hover');
400            }
401        }).mouseout(function() {
402            var element = $(this);
403            if(!element.hasClass('ui-state-active')&&!element.hasClass('ui-state-disabled')) {
404                element.removeClass('ui-state-hover');
405            }
406        }).click(function(e) {
407            var element = $(this);
408            if(!element.hasClass('ui-state-disabled')) {
409                var tabIndex = $this.headers.index(element);
410
411                if(element.hasClass('ui-state-active')) {
412                    $this.unselect(tabIndex);
413                }
414                else {
415                    $this.select(tabIndex);
416                    $(this).trigger('focus.accordion');
417                }
418            }
419
420            e.preventDefault();
421        });
422
423        this.bindKeyEvents();
424    },
425
426    bindKeyEvents: function() {
427        this.headers.on('focus.accordion', function(){
428            $(this).addClass('ui-tabs-outline');
429        })
430        .on('blur.accordion', function(){
431            $(this).removeClass('ui-tabs-outline');
432        })
433        .on('keydown.accordion', function(e) {
434            var keyCode = $.ui.keyCode,
435            key = e.which;
436
437            if(key === keyCode.SPACE || key === keyCode.ENTER) {
438                $(this).trigger('click');
439                e.preventDefault();
440            }
441        });
442    },
443
444    markLoadedPanels: function() {
445        if(this.cfg.multiple) {
446            for(var i = 0; i < this.cfg.active.length; i++) {
447                if(this.cfg.active[i] >= 0)
448                    this.markAsLoaded(this.panels.eq(this.cfg.active[i]));
449            }
450        } else {
451            if(this.cfg.active >= 0)
452                this.markAsLoaded(this.panels.eq(this.cfg.active));
453        }
454    },
455
456    /**
457     *  Activates a tab with given index
458     */
459    select: function(index) {
460        var panel = this.panels.eq(index);
461
462        //Call user onTabChange callback
463        if(this.cfg.onTabChange) {
464            var result = this.cfg.onTabChange.call(this, panel);
465            if(result === false)
466                return false;
467        }
468
469        var shouldLoad = this.cfg.dynamic && !this.isLoaded(panel);
470
471        //update state
472        if(this.cfg.multiple)
473            this.addToSelection(index);
474        else
475            this.cfg.active = index;
476
477        this.saveState();
478
479        if(shouldLoad) {
480            this.loadDynamicTab(panel);
481        }
482        else {
483            if(this.cfg.controlled) {
484                this.fireTabChangeEvent(panel);
485            }
486            else {
487                this.show(panel);
488
489                this.fireTabChangeEvent(panel);
490            }
491
492        }
493
494        return true;
495    },
496
497    /**
498     *  Deactivates a tab with given index
499     */
500    unselect: function(index) {
501        if(this.cfg.controlled) {
502            this.fireTabCloseEvent(index);
503        }
504        else {
505            this.hide(index);
506
507            this.fireTabCloseEvent(index);
508        }
509    },
510
511    show: function(panel) {
512        var _self = this;
513
514        //deactivate current
515        if(!this.cfg.multiple) {
516            var oldHeader = this.headers.filter('.ui-state-active');
517            oldHeader.children('.ui-icon').removeClass(this.cfg.expandedIcon).addClass(this.cfg.collapsedIcon);
518            oldHeader.attr('aria-selected', false);
519            oldHeader.attr('aria-expanded', false).removeClass('ui-state-active ui-corner-top').addClass('ui-corner-all')
520                .next().attr('aria-hidden', true).slideUp(function(){
521                    if(_self.cfg.onTabClose)
522                        _self.cfg.onTabClose.call(_self, panel);
523                });
524        }
525
526        //activate selected
527        var newHeader = panel.prev();
528        newHeader.attr('aria-selected', true);
529        newHeader.attr('aria-expanded', true).addClass('ui-state-active ui-corner-top').removeClass('ui-state-hover ui-corner-all')
530                .children('.ui-icon').removeClass(this.cfg.collapsedIcon).addClass(this.cfg.expandedIcon);
531
532        panel.attr('aria-hidden', false).slideDown('normal', function() {
533            _self.postTabShow(panel);
534        });
535    },
536
537    hide: function(index) {
538        var _self = this,
539        panel = this.panels.eq(index),
540        header = panel.prev();
541
542        header.attr('aria-selected', false);
543        header.attr('aria-expanded', false).children('.ui-icon').removeClass(this.cfg.expandedIcon).addClass(this.cfg.collapsedIcon);
544        header.removeClass('ui-state-active ui-corner-top').addClass('ui-corner-all');
545        panel.attr('aria-hidden', true).slideUp(function(){
546            if(_self.cfg.onTabClose)
547                _self.cfg.onTabClose.call(_self, panel);
548        });
549
550        this.removeFromSelection(index);
551        this.saveState();
552    },
553
554    loadDynamicTab: function(panel) {
555        var $this = this,
556        options = {
557            source: this.id,
558            process: this.id,
559            update: this.id,
560            params: [
561                {name: this.id + '_contentLoad', value: true},
562                {name: this.id + '_newTab', value: panel.attr('id')},
563                {name: this.id + '_tabindex', value: parseInt(panel.index() / 2)}
564            ],
565            onsuccess: function(responseXML, status, xhr) {
566                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
567                        widget: $this,
568                        handle: function(content) {
569                            panel.html(content);
570
571                            if(this.cfg.cache) {
572                                this.markAsLoaded(panel);
573                            }
574                        }
575                    });
576
577                return true;
578            },
579            oncomplete: function() {
580                $this.show(panel);
581            }
582        };
583
584        if(this.hasBehavior('tabChange')) {
585            this.callBehavior('tabChange', options);
586        }
587        else {
588            PrimeFaces.ajax.Request.handle(options);
589        }
590    },
591
592    fireTabChangeEvent : function(panel) {
593        if(this.hasBehavior('tabChange')) {
594            var ext = {
595                params: [
596                    {name: this.id + '_newTab', value: panel.attr('id')},
597                    {name: this.id + '_tabindex', value: parseInt(panel.index() / 2)}
598                ]
599            };
600
601            if(this.cfg.controlled) {
602                var $this = this;
603                ext.oncomplete = function(xhr, status, args, data) {
604                    if(args.access && !args.validationFailed) {
605                        $this.show(panel);
606                    }
607                };
608            }
609
610            this.callBehavior('tabChange', ext);
611        }
612    },
613
614    fireTabCloseEvent : function(index) {
615        if(this.hasBehavior('tabClose')) {
616            var panel = this.panels.eq(index),
617            ext = {
618                params: [
619                    {name: this.id + '_tabId', value: panel.attr('id')},
620                    {name: this.id + '_tabindex', value: parseInt(index)}
621                ]
622            };
623
624            if(this.cfg.controlled) {
625                var $this = this;
626                ext.oncomplete = function(xhr, status, args, data) {
627                    if(args.access && !args.validationFailed) {
628                        $this.hide(index);
629                    }
630                };
631            }
632
633            this.callBehavior('tabClose', ext);
634        }
635    },
636
637    markAsLoaded: function(panel) {
638        panel.data('loaded', true);
639    },
640
641    isLoaded: function(panel) {
642        return panel.data('loaded') == true;
643    },
644
645    addToSelection: function(nodeId) {
646        this.cfg.active.push(nodeId);
647    },
648
649    removeFromSelection: function(nodeId) {
650        this.cfg.active = $.grep(this.cfg.active, function(r) {
651            return r != nodeId;
652        });
653    },
654
655    saveState: function() {
656        if(this.cfg.multiple)
657            this.stateHolder.val(this.cfg.active.join(','));
658        else
659            this.stateHolder.val(this.cfg.active);
660    },
661
662    postTabShow: function(newPanel) {
663        //Call user onTabShow callback
664        if(this.cfg.onTabShow) {
665            this.cfg.onTabShow.call(this, newPanel);
666        }
667
668        PrimeFaces.invokeDeferredRenders(this.id);
669    }
670
671});
672
673/**
674 * PrimeFaces AutoComplete Widget
675 */
676PrimeFaces.widget.AutoComplete = PrimeFaces.widget.BaseWidget.extend({
677
678    init: function(cfg) {
679        this._super(cfg);
680
681        this.panelId = this.jqId + '_panel';
682        this.input = $(this.jqId + '_input');
683        this.hinput = $(this.jqId + '_hinput');
684        this.panel = this.jq.children(this.panelId);
685        this.dropdown = this.jq.children('.ui-button');
686        this.active = true;
687        this.cfg.pojo = this.hinput.length == 1;
688        this.cfg.minLength = this.cfg.minLength != undefined ? this.cfg.minLength : 1;
689        this.cfg.cache = this.cfg.cache||false;
690        this.cfg.resultsMessage = this.cfg.resultsMessage||' results are available, use up and down arrow keys to navigate';
691        this.cfg.ariaEmptyMessage = this.cfg.emptyMessage||'No search results are available.';
692        this.cfg.dropdownMode = this.cfg.dropdownMode||'blank';
693        this.cfg.autoHighlight = (this.cfg.autoHighlight === undefined) ? true : this.cfg.autoHighlight;
694        this.cfg.myPos = this.cfg.myPos||'left top';
695        this.cfg.atPos = this.cfg.atPos||'left bottom';
696        this.cfg.active = (this.cfg.active === false) ? false : true;
697        this.cfg.dynamic = this.cfg.dynamic === true ? true : false;
698        this.cfg.autoSelection = this.cfg.autoSelection === false ? false : true;
699        this.cfg.escape = this.cfg.escape === false ? false : true;
700        this.suppressInput = true;
701        this.touchToDropdownButton = false;
702        this.isTabPressed = false;
703        this.isDynamicLoaded = false;
704        this.currentInputValue = '';
705        
706        this.cfg.onChange = this.input.prop('onchange');
707        this.input.prop('onchange', null).off('change');
708
709        if(this.cfg.cache) {
710            this.initCache();
711        }
712        
713        if (this.cfg.queryMode !== 'server') {
714            this.fetchItems();
715        }
716
717        //pfs metadata
718        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
719        this.hinput.data(PrimeFaces.CLIENT_ID_DATA, this.id);
720        
721        this.placeholder = this.input.attr('placeholder');
722
723        if(this.cfg.multiple) {
724            this.setupMultipleMode();
725
726            this.multiItemContainer.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
727
728            if(this.cfg.selectLimit >= 0 && this.multiItemContainer.children('li.ui-autocomplete-token').length === this.cfg.selectLimit) {
729                this.input.hide();
730                this.disableDropdown();
731            }
732        }
733        else {
734            //visuals
735            PrimeFaces.skinInput(this.input);
736
737            this.input.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
738            this.dropdown.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
739        }
740
741        //core events
742        this.bindStaticEvents();
743
744        //client Behaviors
745        if(this.cfg.behaviors) {
746            PrimeFaces.attachBehaviors(this.input, this.cfg.behaviors);
747        }
748
749        //force selection
750        if(this.cfg.forceSelection) {
751            this.setupForceSelection();
752        }
753
754        //Panel management
755        if(this.panel.length) {
756            this.appendPanel();
757        }
758
759        //itemtip
760        if(this.cfg.itemtip) {
761            this.itemtip = $('<div id="' + this.id + '_itemtip" class="ui-autocomplete-itemtip ui-state-highlight ui-widget ui-corner-all ui-shadow"></div>').appendTo(document.body);
762            this.cfg.itemtipMyPosition = this.cfg.itemtipMyPosition||'left top';
763            this.cfg.itemtipAtPosition = this.cfg.itemtipAtPosition||'right bottom';
764            this.cfg.checkForScrollbar = (this.cfg.itemtipAtPosition.indexOf('right') !== -1);
765        }
766
767        //aria
768        this.input.attr('aria-autocomplete', 'list');
769        this.jq.attr('role', 'application');
770        this.jq.append('<span role="status" aria-live="polite" class="ui-autocomplete-status ui-helper-hidden-accessible"></span>');
771        this.status = this.jq.children('.ui-autocomplete-status');
772    },
773
774    //@override
775    refresh: function(cfg) {
776        this._super(cfg);
777    },
778
779    appendPanel: function() {
780        PrimeFaces.utils.registerDynamicOverlay(this, this.panel, this.id + '_panel');
781    },
782
783    initCache: function() {
784        this.cache = {};
785        var $this=this;
786
787        this.cacheTimeout = setInterval(function(){
788            $this.clearCache();
789        }
789, this.cfg.cacheTimeout);
790    },
791
792    clearCache: function() {
793        this.cache = {};
794    },
795
796    /**
797     * Binds events for multiple selection mode
798     */
799    setupMultipleMode: function() {
800        var $this = this;
801        this.multiItemContainer = this.jq.children('ul');
802        this.inputContainer = this.multiItemContainer.children('.ui-autocomplete-input-token');
803
804        this.multiItemContainer.hover(function() {
805                $(this).addClass('ui-state-hover');
806            },
807            function() {
808                $(this).removeClass('ui-state-hover');
809            }
810        ).click(function() {
811            $this.input.focus();
812        });
813
814        //delegate events to container
815        this.input.focus(function() {
816            $this.multiItemContainer.addClass('ui-state-focus');
817        }).blur(function(e) {
818            $this.multiItemContainer.removeClass('ui-state-focus');
819        });
820
821        var closeSelector = '> li.ui-autocomplete-token > .ui-autocomplete-token-icon';
822        this.multiItemContainer.off('click', closeSelector).on('click', closeSelector, null, function(event) {
823            if($this.multiItemContainer.children('li.ui-autocomplete-token').length === $this.cfg.selectLimit) {
824                $this.input.css('display', 'inline');
825                $this.enableDropdown();
826            }
827            $this.removeItem(event, $(this).parent());
828        });
829    },
830
831    bindStaticEvents: function() {
832        var $this = this;
833
834        this.bindKeyEvents();
835
836        this.bindDropdownEvents();
837
838        if(PrimeFaces.env.browser.mobile) {
839            this.dropdown.bind('touchstart', function() {
840                $this.touchToDropdownButton = true;
841            });
842        }
843
844        PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.panel,
845            function() { return $this.itemtip; },
846            function(e, eventTarget) {
847                if(!($this.panel.is(eventTarget) || $this.panel.has(eventTarget).length > 0)) {
848                    $this.hide();
849                }
850            });
851
852        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.panel, function() {
853             $this.alignPanel();
854        });
855    },
856
857    bindDropdownEvents: function() {
858        var $this = this;
859
860        PrimeFaces.skinButton(this.dropdown);
861
862        this.dropdown.mouseup(function() {
863            if($this.active) {
864                $this.searchWithDropdown();
865                $this.input.focus();
866            }
867        }).keyup(function(e) {
868            var keyCode = $.ui.keyCode,
869            key = e.which;
870
871            if(key === keyCode.SPACE || key === keyCode.ENTER) {
872                $this.searchWithDropdown();
873                $this.input.focus();
874                e.preventDefault();
875                e.stopPropagation();
876            }
877        });
878    },
879
880    disableDropdown: function() {
881        if(this.dropdown.length) {
882            this.dropdown.off().prop('disabled', true).addClass('ui-state-disabled');
883        }
884    },
885
886    enableDropdown: function() {
887        if(this.dropdown.length && this.dropdown.prop('disabled')) {
888            this.bindDropdownEvents();
889            this.dropdown.prop('disabled', false).removeClass('ui-state-disabled');
890        }
891    },
892
893    bindKeyEvents: function() {
894        var $this = this;
895
896        if(this.cfg.queryEvent !== 'enter') {
897            this.input.on('input propertychange', function(e) {
898                $this.processKeyEvent(e);
899            });
900        }
901
902        this.input.on('keyup.autoComplete', function(e) {
903            var keyCode = $.ui.keyCode,
904            key = e.which;
905
906            if(PrimeFaces.env.isIE(9) && (key === keyCode.BACKSPACE || key === keyCode.DELETE)) {
907                $this.processKeyEvent(e);
908            }
909
910            if($this.cfg.queryEvent === 'enter' && (key === keyCode.ENTER)) {
911                if($this.itemSelectedWithEnter)
912                    $this.itemSelectedWithEnter = false;
913                else
914                    $this.search($this.input.val());
915            }
916
917            if($this.panel.is(':visible')) {
918                if(key === keyCode.ESCAPE) {
919                    $this.hide();
920                }
921                else if(key === keyCode.UP || key === keyCode.DOWN) {
922                    var highlightedItem = $this.items.filter('.ui-state-highlight');
923                    if(highlightedItem.length) {
924                        $this.displayAriaStatus(highlightedItem.data('item-label'));
925                    }
926                }
927            }
928
929            $this.checkMatchedItem = true;
930            $this.isTabPressed = false;
931
932        }).on('keydown.autoComplete', function(e) {
933            var keyCode = $.ui.keyCode;
934
935            $this.suppressInput = false;
936            if($this.panel.is(':visible')) {
937                var highlightedItem = $this.items.filter('.ui-state-highlight');
938
939                switch(e.which) {
940                    case keyCode.UP:
941                        var prev = highlightedItem.length == 0 ? $this.items.eq(0) : highlightedItem.prevAll('.ui-autocomplete-item:first');
942
943                        if(prev.length == 1) {
944                            highlightedItem.removeClass('ui-state-highlight');
945                            prev.addClass('ui-state-highlight');
946
947                            if($this.cfg.scrollHeight) {
948                                PrimeFaces.scrollInView($this.panel, prev);
949                            }
950
951                            if($this.cfg.itemtip) {
952                                $this.showItemtip(prev);
953                            }
954                        }
955
956                        e.preventDefault();
957                        break;
958
959                    case keyCode.DOWN:
960                        var next = highlightedItem.length == 0 ? $this.items.eq(0) : highlightedItem.nextAll('.ui-autocomplete-item:first');
961
962                        if(next.length == 1) {
963                            highlightedItem.removeClass('ui-state-highlight');
964                            next.addClass('ui-state-highlight');
965
966                            if($this.cfg.scrollHeight) {
967                                PrimeFaces.scrollInView($this.panel, next);
968                            }
969
970                            if($this.cfg.itemtip) {
971                                $this.showItemtip(next);
972                            }
973                        }
974
975                        e.preventDefault();
976                        break;
977
978                    case keyCode.ENTER:
979                        if ($this.timeout) {
980                            $this.deleteTimeout();
981                        }
982
983                        if (highlightedItem.length > 0) {
984                            $this.preventInputChangeEvent = true;
985                            highlightedItem.trigger('click');
986                            $this.itemSelectedWithEnter = true;
987                        }
988
989                        e.preventDefault();
990                        e.stopPropagation();
991
992                        break;
993
994                    case 18: //keyCode.ALT:
995                    case 224:
996                        break;
997
998                    case keyCode.TAB:
999                        if(highlightedItem.length && $this.cfg.autoSelection) {
1000                            highlightedItem.trigger('click');
1001                        } else {
1002                            $this.hide();
1003                            if ($this.timeout) {
1004                                $this.deleteTimeout();
1005                            }
1006                        }
1007                        $this.isTabPressed = true;
1008                        break;
1009                }
1010            }
1011            else {
1012                switch(e.which) {
1013                    case keyCode.TAB:
1014                        if ($this.timeout) {
1015                            $this.deleteTimeout();
1016                        }
1017                        $this.isTabPressed = true;
1018                    break;
1019
1020                    case keyCode.ENTER:
1021                        if($this.cfg.queryEvent === 'enter' || ($this.timeout > 0) || $this.querying) {
1022                            e.preventDefault();
1023                        }
1024
1025                        if($this.cfg.queryEvent !== 'enter') {
1026                            $this.isValid($(this).val());
1027                        }
1028                    break;
1029
1030                    case keyCode.BACKSPACE:
1031                        if ($this.cfg.multiple && !$this.input.val().length) {
1032                            $this.removeItem(e, $(this).parent().prev());
1033
1034                            e.preventDefault();
1035                        }
1036                    break;
1037                };
1038            }
1039
1040        }).on('paste.autoComplete', function() {
1041            $this.suppressInput = false;
1042            $this.checkMatchedItem = true;
1043	}).on('change.autoComplete', function(e) {
1044            var value = e.currentTarget.value,
1045            valid = $this.isValid(value);
1046
1047            if ($this.cfg.forceSelection && $this.currentInputValue === '' && !valid) {
1048                $this.preventInputChangeEvent = true;
1049            }
1050
1051            if ($this.cfg.onChange && !$this.preventInputChangeEvent) {
1052                $this.cfg.onChange.call(this);
1053            }
1054
1055            $this.currentInputValue = $this.cfg.forceSelection && !valid ? '' : value;
1056            $this.preventInputChangeEvent = false;
1057        });
1058    },
1059
1060    bindDynamicEvents: function() {
1061        var $this = this;
1062
1063        //visuals and click handler for items
1064        this.items.on('mouseover', function() {
1065            var item = $(this);
1066
1067            if(!item.hasClass('ui-state-highlight')) {
1068                $this.items.filter('.ui-state-highlight').removeClass('ui-state-highlight');
1069                item.addClass('ui-state-highlight');
1070
1071                if($this.cfg.itemtip) {
1072                    $this.showItemtip(item);
1073                }
1074            }
1075        })
1076        .on('click', function(event) {
1077            var item = $(this),
1078            isMoreText = item.hasClass('ui-autocomplete-moretext');
1079
1080            if(isMoreText) {
1081                $this.input.focus();
1082                $this.invokeMoreTextBehavior();
1083            }
1084            else {
1085                var itemValue = item.attr('data-item-value');
1086
1087                if($this.cfg.multiple) {
1088                    var found = false;
1089                    if($this.cfg.unique) {
1090                        found = $this.multiItemContainer.children("li[data-token-value='" + $.escapeSelector(itemValue) + "']").length != 0;
1091                    }
1092
1093                    if(!found) {
1094                        var itemStyleClass = item.attr('data-item-class');
1095                        var itemDisplayMarkup = '<li data-token-value="' + PrimeFaces.escapeHTML(itemValue);
1096                        itemDisplayMarkup += '"class="ui-autocomplete-token ui-state-active ui-corner-all ui-helper-hidden';
1097                        itemDisplayMarkup += (itemStyleClass === '' ? '' : ' '+itemStyleClass) + '">';
1098                        itemDisplayMarkup += '<span class="ui-autocomplete-token-icon ui-icon ui-icon-close"></span>';
1099                        itemDisplayMarkup += '<span class="ui-autocomplete-token-label">' + PrimeFaces.escapeHTML(item.attr('data-item-label')) + '</span></li>';
1100
1101                        $this.inputContainer.before(itemDisplayMarkup);
1102                        $this.multiItemContainer.children('.ui-helper-hidden').fadeIn();
1103                        $this.input.val('');
1104                        $this.input.removeAttr('placeholder');
1105
1106                        $this.hinput.append('<option value="' + PrimeFaces.escapeHTML(itemValue) + '" selected="selected"></option>');
1107                        if($this.multiItemContainer.children('li.ui-autocomplete-token').length >= $this.cfg.selectLimit) {
1108                            $this.input.css('display', 'none').blur();
1109                            $this.disableDropdown();
1110                        }
1111
1112                        $this.invokeItemSelectBehavior(event, itemValue);
1113                    }
1114                }
1115                else {
1116                    $this.input.val(item.attr('data-item-label'));
1117
1118                    this.currentText = $this.input.val();
1119                    this.previousText = $this.input.val();
1120
1121                    if($this.cfg.pojo) {
1122                        $this.hinput.val(itemValue);
1123                    }
1124
1125                    if(PrimeFaces.env.isLtIE(10)) {
1126                        var length = $this.input.val().length;
1127                        $this.input.setSelection(length,length);
1128                    }
1129
1130                    $this.invokeItemSelectBehavior(event, itemValue);
1131                }
1132                
1133                if ($this.cfg.onChange) {
1134                    $this.cfg.onChange.call(this);
1135                }
1136
1137                if (!$this.isTabPressed) {
1138                    $this.input.focus();
1139                }
1140            }
1141
1142            $this.hide();
1143        })
1144        .on('mousedown', function() {
1145            $this.preventInputChangeEvent = true;
1146            $this.checkMatchedItem = false;
1147        });
1148
1149        if(PrimeFaces.env.browser.mobile) {
1150            this.items.bind('touchstart', function() {
1151                if(!$this.touchToDropdownButton) {
1152                    $this.itemClick = true;
1153                }
1154            });
1155        }
1156    },
1157
1158    processKeyEvent: function(e) {
1159        var $this = this;
1160
1161        if($this.suppressInput) {
1162            e.preventDefault();
1163            return;
1164        }
1165
1166        // for touch event on mobile
1167        if(PrimeFaces.env.browser.mobile) {
1168            $this.touchToDropdownButton = false;
1169            if($this.itemClick) {
1170                $this.itemClick = false;
1171                return;
1172            }
1173        }
1174
1175        var value = $this.input.val();
1176
1177        if($this.cfg.pojo && !$this.cfg.multiple) {
1178            $this.hinput.val(value);
1179        }
1180
1181        if(!value.length) {
1182            $this.hide();
1183            $this.deleteTimeout();
1184        }
1185
1186        if(value.length >= $this.cfg.minLength) {
1187            if($this.timeout) {
1188                $this.deleteTimeout();
1189            }
1190
1191            var delay = $this.cfg.delay;
1192            $this.timeout = setTimeout(function() {
1193                $this.timeout = null;
1194                $this.search(value);
1195            }, delay);
1196        }
1197        else if(value.length === 0) {
1198            if($this.timeout) {
1199                $this.deleteTimeout();
1200            }
1201            $this.fireClearEvent();
1202        }
1203    },
1204
1205    showItemtip: function(item) {
1206        if(item.hasClass('ui-autocomplete-moretext')) {
1207            this.itemtip.hide();
1208        }
1209        else {
1210            var content;
1211            if(item.is('li')) {
1212                content = item.next('.ui-autocomplete-itemtip-content');
1213            } else {
1214                if(item.children('td:last').hasClass('ui-autocomplete-itemtip-content')) {
1215                    content = item.children('td:last');
1216                } else {
1217                    this.itemtip.hide();
1218                    return;
1219                }
1220            }
1221
1222            this.itemtip.html(content.html())
1223                        .css({
1224                            'left':'',
1225                            'top':'',
1226                            'z-index': ++PrimeFaces.zindex,
1227                            'width': content.outerWidth()
1228                        })
1229                        .position({
1230                            my: this.cfg.itemtipMyPosition
1231                            ,at: this.cfg.itemtipAtPosition
1232                            ,of: item
1233                        });
1234
1235            //scrollbar offset
1236            if(this.cfg.checkForScrollbar) {
1237                if(this.panel.innerHeight() < this.panel.children('.ui-autocomplete-items').outerHeight(true)) {
1238                    var panelOffset = this.panel.offset();
1239                    this.itemtip.css('left', panelOffset.left + this.panel.outerWidth());
1240                }
1241            }
1242
1243            this.itemtip.show();
1244        }
1245    },
1246
1247    showSuggestions: function(query) {
1248        this.items = this.panel.find('.ui-autocomplete-item');
1249        this.items.attr('role', 'option');
1250
1251        if(this.cfg.grouping) {
1252            this.groupItems();
1253        }
1254
1255        this.bindDynamicEvents();
1256
1257        var $this=this,
1258        hidden = this.panel.is(':hidden');
1259
1260        if(hidden) {
1261            this.show();
1262        }
1263        else {
1264            this.alignPanel();
1265        }
1266
1267        if(this.items.length > 0) {
1268            var firstItem = this.items.eq(0);
1269
1270            //highlight first item
1271            if(this.cfg.autoHighlight && firstItem.length) {
1272                firstItem.addClass('ui-state-highlight');
1273            }
1274
1275            //highlight query string
1276            if(this.panel.children().is('ul') && query.length > 0) {
1277                this.items.filter(':not(.ui-autocomplete-moretext)').each(function() {
1278                    var item = $(this);
1279                    var text = $this.cfg.escape ? item.html() : item.text();
1280                    var re = new RegExp(PrimeFaces.escapeRegExp(query), 'gi'),
1281                    highlighedText = text.replace(re, '<span class="ui-autocomplete-query">$&</span>');
1282
1283                    item.html(highlighedText);
1284                });
1285            }
1286
1287            if(this.cfg.forceSelection) {
1288                this.currentItems = [];
1289                this.items.each(function(i, item) {
1290                    $this.currentItems.push($(item).attr('data-item-label'));
1291                });
1292            }
1293
1294            //show itemtip if defined
1295            if(this.cfg.autoHighlight && this.cfg.itemtip && firstItem.length === 1) {
1296                this.showItemtip(firstItem);
1297            }
1298
1299            this.displayAriaStatus(this.items.length + this.cfg.resultsMessage);
1300        }
1301        else {
1302            if(this.cfg.emptyMessage) {
1303                var emptyText = '<div class="ui-autocomplete-emptyMessage ui-widget">' + PrimeFaces.escapeHTML(this.cfg.emptyMessage) + '</div>';
1304                this.panel.html(emptyText);
1305            }
1306            else {
1307                this.panel.hide();
1308            }
1309
1310            this.displayAriaStatus(this.cfg.ariaEmptyMessage);
1311        }
1312    },
1313
1314    searchWithDropdown: function() {
1315        this.isSearchWithDropdown = true;
1316        
1317        if(this.cfg.dropdownMode === 'current')
1318            this.search(this.input.val());
1319        else
1320            this.search('');
1321    },
1322
1323    search: function(query) {
1324        //allow empty string but not undefined or null
1325        if (!this.cfg.active || query === undefined || query === null) {
1326            return;
1327        }
1328
1329        if (this.cfg.cache && !(this.cfg.dynamic && !this.isDynamicLoaded)) {
1330            if (this.cache[query]) {
1331                this.panel.html(this.cache[query]);
1332                this.showSuggestions(query);
1333                return;
1334            }
1335            else if (this.cfg.queryMode === 'client') {
1336                if (this.isSearchWithDropdown) {
1337                    var suggestions = this.wrapperStartTag,
1338                        re = new RegExp(this.wrapperStartTag + '|' + this.wrapperEndTag, 'g');
1339                    Object.entries(this.cache).map(function(item) {
1340                        suggestions += item[1].replace(re, '');
1341                    });
1342                    suggestions += this.wrapperEndTag;
1343                    
1344                    this.panel.html(suggestions);
1345                    
1346                    this.isSearchWithDropdown = false;
1347                }
1348                else {
1349                    this.panel.empty();
1350                }
1351                
1352                this.showSuggestions(query);
1353                return;
1354            }
1355        }
1356
1357        if (!this.active) {
1358            return;
1359        }
1360
1361        this.querying = true;
1362
1363        var $this = this;
1364
1365        if (this.cfg.itemtip) {
1366            this.itemtip.hide();
1367        }
1368
1369        var options = {
1370            source: this.id,
1371            process: this.id,
1372            update: this.id,
1373            formId: this.cfg.formId,
1374            onsuccess: function(responseXML, status, xhr) {
1375                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
1376                    widget: $this,
1377                    handle: function(content) {
1378                        if(this.cfg.dynamic && !this.isDynamicLoaded) {
1379                            this.panel = $(content);
1380                            this.appendPanel();
1381                            content = this.panel.get(0).innerHTML;
1382                        }
1383                        else {
1384                            this.panel.html(content);
1385                        }
1386
1387                        if (this.cfg.cache) {
1388                            if (this.cfg.queryMode !== 'server' && !this.isDynamicLoaded && this.cache[query]) {
1389                                this.panel.html(this.cache[query]);
1390                            }
1391                            else {
1392                                this.cache[query] = content;
1393                            }
1394                        }
1395
1396                        this.showSuggestions(query);
1397                    }
1398                });
1399
1400                return true;
1401            },
1402            oncomplete: function() {
1403                $this.querying = false;
1404                $this.isDynamicLoaded = true;
1405            }
1406        };
1407
1408        options.params = [
1409          {name: this.id + '_query', value: query}
1410        ];
1411        
1412        if (this.cfg.queryMode === 'hybrid') {
1413            options.params.push({name: this.id + '_clientCache', value: true});
1414        }
1415
1416        if (this.cfg.dynamic && !this.isDynamicLoaded) {
1417            options.params.push({name: this.id + '_dynamicload', value: true});
1418        }
1419
1420        if (this.hasBehavior('query')) {
1421            this.callBehavior('query', options);
1422        }
1423        else {
1424            PrimeFaces.ajax.Request.handle(options);
1425        }
1426    },
1427
1428    show: function() {
1429        this.alignPanel();
1430
1431        if(this.cfg.effect)
1432            this.panel.show(this.cfg.effect, {}, this.cfg.effectDuration);
1433        else
1434            this.panel.show();
1435    },
1436
1437    hide: function() {
1438        this.panel.hide();
1439        this.panel.css('height', 'auto');
1440
1441        if(this.cfg.itemtip) {
1442            this.itemtip.hide();
1443        }
1444    },
1445
1446    invokeItemSelectBehavior: function(event, itemValue) {
1447        if(this.hasBehavior('itemSelect')) {
1448            var ext = {
1449                params : [
1450                    {name: this.id + '_itemSelect', value: itemValue}
1451                ]
1452            };
1453
1454            this.callBehavior('itemSelect', ext);
1455        }
1456    },
1457
1458    invokeItemUnselectBehavior: function(event, itemValue) {
1459        if(this.hasBehavior('itemUnselect')) {
1460            var ext = {
1461                params : [
1462                    {name: this.id + '_itemUnselect', value: itemValue}
1463                ]
1464            };
1465
1466            this.callBehavior('itemUnselect', ext);
1467        }
1468    },
1469
1470    invokeMoreTextBehavior: function() {
1471        if(this.hasBehavior('moreText')) {
1472            var ext = {
1473                params : [
1474                    {name: this.id + '_moreText', value: true}
1475                ]
1476            };
1477
1478            this.callBehavior('moreText', ext);
1479        }
1480    },
1481
1482    removeItem: function(event, item) {
1483        var itemValue = item.attr('data-token-value'),
1484        itemIndex = this.multiItemContainer.children('li.ui-autocomplete-token').index(item),
1485        $this = this;
1486
1487        //remove from options
1488        this.hinput.children('option').eq(itemIndex).remove();
1489
1490        //remove from items
1491        item.fadeOut('fast', function() {
1492            var token = $(this);
1493
1494            token.remove();
1495
1496            $this.invokeItemUnselectBehavior(event, itemValue);
1497        });
1498        
1499        // if empty return placeholder
1500        if (this.placeholder && this.hinput.children('option').length === 0) {
1501            this.input.attr('placeholder', this.placeholder);
1502        }
1503    },
1504
1505    setupForceSelection: function() {
1506        this.currentItems = [this.input.val()];
1507        var $this = this;
1508
1509        this.input.blur(function() {
1510            var value = $(this).val(),
1511            valid = $this.isValid(value);
1512
1513            if($this.cfg.autoSelection && valid && $this.checkMatchedItem && $this.items && !$this.isTabPressed && !$this.itemSelectedWithEnter) {
1514                var selectedItem = $this.items.filter('[data-item-label="' + $.escapeSelector(value) + '"]');
1515                if (selectedItem.length) {
1516                    selectedItem.click();
1517                }
1518            }
1519
1520            $this.checkMatchedItem = false;
1521        });
1522    },
1523
1524    disable: function() {
1525        this.input.addClass('ui-state-disabled').prop('disabled', true);
1526
1527        if(this.dropdown.length) {
1528            this.dropdown.addClass('ui-state-disabled').prop('disabled', true);
1529        }
1530    },
1531
1532    enable: function() {
1533        this.input.removeClass('ui-state-disabled').prop('disabled', false);
1534
1535        if(this.dropdown.length) {
1536            this.dropdown.removeClass('ui-state-disabled').prop('disabled', false);
1537        }
1538    },
1539
1540    close: function() {
1541        this.hide();
1542    },
1543
1544    deactivate: function() {
1545        this.active = false;
1546    },
1547
1548    activate: function() {
1549        this.active = true;
1550    },
1551
1552    alignPanel: function() {
1553        var panelWidth = null;
1554
1555        if(this.cfg.multiple) {
1556            panelWidth = this.multiItemContainer.outerWidth();
1557        }
1558        else {
1559            if(this.panel.is(':visible')) {
1560                panelWidth = this.panel.children('.ui-autocomplete-items').outerWidth();
1561            }
1562            else {
1563                this.panel.css({'visibility':'hidden','display':'block'});
1564                panelWidth = this.panel.children('.ui-autocomplete-items').outerWidth();
1565                this.panel.css({'visibility':'visible','display':'none'});
1566            }
1567
1568            var inputWidth = this.input.outerWidth();
1569            if(panelWidth < inputWidth) {
1570                panelWidth = inputWidth;
1571            }
1572        }
1573
1574        if(this.cfg.scrollHeight) {
1575            var heightConstraint = this.panel.is(':hidden') ? this.panel.height() : this.panel.children().height();
1576            if(heightConstraint > this.cfg.scrollHeight)
1577                this.panel.height(this.cfg.scrollHeight);
1578            else
1579                this.panel.css('height', 'auto');
1580        }
1581
1582        this.panel.css({'left':'',
1583                        'top':'',
1584                        'width': panelWidth,
1585                        'z-index': ++PrimeFaces.zindex
1586                });
1587
1588        if(this.panel.parent().is(this.jq)) {
1589            this.panel.css({
1590                left: 0,
1591                top: this.jq.innerHeight()
1592            });
1593        }
1594        else {
1595            this.panel.position({
1596                    my: this.cfg.myPos
1597                    ,at: this.cfg.atPos
1598                    ,of: this.cfg.multiple ? this.jq : this.input
1599                    ,collision: 'flipfit'
1600                });
1601        }
1602    },
1603
1604    displayAriaStatus: function(text) {
1605        this.status.html('<div>' + PrimeFaces.escapeHTML(text) + '</div>');
1606    },
1607
1608    groupItems: function() {
1609        var $this = this;
1610
1611        if(this.items.length) {
1612            this.itemContainer = this.panel.children('.ui-autocomplete-items');
1613
1614            var firstItem = this.items.eq(0);
1615            if(!firstItem.hasClass('ui-autocomplete-moretext')) {
1616                this.currentGroup = firstItem.data('item-group');
1617                var currentGroupTooltip = firstItem.data('item-group-tooltip');
1618
1619                firstItem.before(this.getGroupItem($this.currentGroup, $this.itemContainer, currentGroupTooltip));
1620            }
1621
1622            this.items.filter(':not(.ui-autocomplete-moretext)').each(function(i) {
1623                var item = $this.items.eq(i),
1624                itemGroup = item.data('item-group'),
1625                itemGroupTooltip = item.data('item-group-tooltip');
1626
1627                if($this.currentGroup !== itemGroup) {
1628                    $this.currentGroup = itemGroup;
1629                    item.before($this.getGroupItem(itemGroup, $this.itemContainer, itemGroupTooltip));
1630                }
1631            });
1632        }
1633    },
1634
1635    getGroupItem: function(group, container, tooltip) {
1636        var element = null;
1637
1638        if(container.is('.ui-autocomplete-table')) {
1639            if(!this.colspan) {
1640                this.colspan = this.items.eq(0).children('td').length;
1641            }
1642
1643            element = $('<tr class="ui-autocomplete-group ui-widget-header"><td colspan="' + this.colspan + '">' + group + '</td></tr>');
1644        }
1645        else {
1646            element = $('<li class="ui-autocomplete-group ui-autocomplete-list-item ui-widget-header">' + group + '</li>');
1647        }
1648
1649        if(element) {
1650            element.attr('title', tooltip);
1651        }
1652
1653        return element;
1654    },
1655
1656    deleteTimeout: function() {
1657        clearTimeout(this.timeout);
1658        this.timeout = null;
1659    },
1660
1661    fireClearEvent: function() {
1662        this.callBehavior('clear');
1663    },
1664
1665    isValid: function(value) {
1666        if(!this.cfg.forceSelection) {
1667            return;
1668        }
1669
1670        var valid = false;
1671
1672        for(var i = 0; i < this.currentItems.length; i++) {
1673            var stripedItem = this.currentItems[i];
1674            if (stripedItem) {
1675                stripedItem = stripedItem.replace(/\r?\n/g, '');
1676            }
1677
1678            if(stripedItem === value) {
1679                valid = true;
1680                break;
1681            }
1682        }
1683
1684        if(!valid) {
1685            this.input.val('');
1686            if(!this.cfg.multiple) {
1687                this.hinput.val('');
1688            }
1689            this.fireClearEvent();
1690        }
1691
1692        return valid;
1693    },
1694    
1695    fetchItems: function() {
1696        var $this = this;
1697        
1698        var options = {
1699            source: this.id,
1700            process: this.id,
1701            update: this.id,
1702            formId: this.cfg.formId,
1703            global: false,
1704            params: [{name: this.id + '_clientCache', value: true}],
1705            onsuccess: function(responseXML, status, xhr) {
1706                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
1707                    widget: $this,
1708                    handle: function(content) {
1709                        $this.setCache($(content));
1710                    }
1711                });
1712
1713                return true;
1714            }
1715        };
1716
1717        PrimeFaces.ajax.Request.handle(options);
1718    },
1719    
1720    setCache: function(wrapper) {
1721        var $this = this,
1722        items = wrapper.find('.ui-autocomplete-item'),
1723        prevKey = null;
1724
1725        if (!this.wrapperStartTag || !this.wrapperEndTag) {
1726            this.findWrapperTag(wrapper);
1727        }
1728
1729        for (var i = 0; i < items.length; i++) {
1730            var item = items.eq(i),
1731            key = item.data('item-key');
1732
1733            this.cache[key] = (this.cache[key] || this.wrapperStartTag) + item.get(0).outerHTML;
1734            
1735            if ((prevKey !== null && prevKey !== key) || (i === items.length - 1)) {
1736                this.cache[prevKey] += $this.wrapperEndTag;
1737            }
1738            
1739            prevKey = key;
1740        }
1741    },
1742    
1743    findWrapperTag: function(wrapper) {
1744        if (wrapper.is('ul')) {
1745            this.wrapperStartTag = '<ul class="ui-autocomplete-items ui-autocomplete-list ui-widget-content ui-widget ui-corner-all ui-helper-reset">';
1746            this.wrapperEndTag = '</ul>';
1747        }
1748        else {
1749            var header = wrapper.find('> table > thead');
1750            this.wrapperStartTag = '<table class="ui-autocomplete-items ui-autocomplete-table ui-widget-content ui-widget ui-corner-all ui-helper-reset">' +
1751                    (header.length ? header.eq(0).outherHTML : '') +
1752                    '<tbody>';
1753            this.wrapperEndTag = '</tbody></table>';
1754        }
1755    }
1756    
1757});
1758
1759/**
1760 * PrimeFaces BlockUI Widget
1761 */
1762PrimeFaces.widget.BlockUI = PrimeFaces.widget.BaseWidget.extend({
1763    
1764    init: function(cfg) {
1765        this.cfg = cfg;
1766        this.id = this.cfg.id;
1767        this.jqId = PrimeFaces.escapeClientId(this.id);
1768        this.block = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.cfg.block);
1769        this.content = $(this.jqId);
1770        this.cfg.animate = (this.cfg.animate === false)? false : true;
1771        this.cfg.blocked = (this.cfg.blocked === true)? true : false;
1772        
1773        this.render();
1774
1775        if(this.cfg.triggers) {
1776            this.bindTriggers();
1777        }
1778        
1779        if(this.cfg.blocked) {
1780            this.show();
1781        }
1782
1783        this.removeScriptElement(this.id);
1784    },
1785            
1786    refresh: function(cfg) {
1787        this.blocker.remove();
1788        this.block.children('.ui-blockui-content').remove();
1789        $(document).off('pfAjaxSend.' + this.id + ' pfAjaxComplete.' + this.id);
1790        
1791        this._super(cfg);
1792    },
1793    
1794    bindTriggers: function() {
1795        var $this = this;
1796        
1797        //listen global ajax send and complete callbacks
1798        $(document).on('pfAjaxSend.' + this.id, function(e, xhr, settings) {
1799            var sourceId = $.type(settings.source) === 'string' ? settings.source : settings.source.name;
1800            // we must evaluate it each time as the DOM might has been changed
1801            var triggers = PrimeFaces.expressions.SearchExpressionFacade.resolve
1801Components($this.cfg.triggers);
1802            
1803            if($.inArray(sourceId, triggers) !== -1 && !$this.cfg.blocked) {
1804                $this.show();
1805            }
1806        });
1807
1808        $(document).on('pfAjaxComplete.' + this.id, function(e, xhr, settings) {
1809            var sourceId = $.type(settings.source) === 'string' ? settings.source : settings.source.name;
1810            // we must evaluate it each time as the DOM might has been changed
1811            var triggers = PrimeFaces.expressions.SearchExpressionFacade.resolveComponents($this.cfg.triggers);
1812            
1813            if($.inArray(sourceId, triggers) !== -1 && !$this.cfg.blocked) {
1814                $this.hide();
1815            }
1816        });
1817    },
1818    
1819    /**
1820     * Show the component with optional duration animation.
1821     * 
1822     * @param [duration] Durations are given in milliseconds; higher values indicate slower animations, not faster ones. 
1823     *                 The strings 'fast' and 'slow' can be supplied to indicate durations of 200 and 600 milliseconds, 
1824     *                 respectively.
1825     */
1826    show: function(duration) {
1827        this.blocker.css('z-index', ++PrimeFaces.zindex);
1828        
1829        //center position of content
1830        for(var i = 0; i < this.block.length; i++) {
1831            var blocker = $(this.blocker[i]),
1832                content = $(this.content[i]);
1833           
1834            content.css({
1835                'left': (blocker.width() - content.outerWidth()) / 2,
1836                'top': (blocker.height() - content.outerHeight()) / 2,
1837                'z-index': ++PrimeFaces.zindex
1838            });
1839        }
1840
1841        var animated = this.cfg.animate;
1842        if(animated)
1843            this.blocker.fadeIn(duration);    
1844        else
1845            this.blocker.show(duration);
1846
1847        if(this.hasContent()) {
1848            if(animated)
1849                this.content.fadeIn(duration);
1850            else
1851                this.content.show(duration);
1852        }
1853        
1854        this.block.attr('aria-busy', true);
1855    },
1856    
1857    /**
1858     * Hide the component with optional duration animation.
1859     * 
1860     * @param [duration] Durations are given in milliseconds; higher values indicate slower animations, not faster ones. 
1861     *                 The strings 'fast' and 'slow' can be supplied to indicate durations of 200 and 600 milliseconds, 
1862     *                 respectively.
1863     */
1864    hide: function(duration) {
1865        var animated = this.cfg.animate;
1866
1867        if(animated)
1868            this.blocker.fadeOut(duration);
1869        else
1870            this.blocker.hide(duration);
1871
1872        if(this.hasContent()) {
1873            if(animated)
1874                this.content.fadeOut(duration);
1875            else
1876                this.content.hide(duration);
1877        }
1878        
1879        this.block.attr('aria-busy', false);
1880    },
1881    
1882    render: function() {   
1883        this.blocker = $('<div id="' + this.id + '_blocker" class="ui-blockui ui-widget-overlay ui-helper-hidden"></div>');
1884
1885        if(this.cfg.styleClass) {
1886            this.blocker.addClass(this.cfg.styleClass);
1887        }
1888
1889        if(this.block.hasClass('ui-corner-all')) {
1890            this.blocker.addClass('ui-corner-all');
1891        }
1892        
1893        if(this.block.length > 1) {
1894            this.content = this.content.clone();
1895        }
1896        
1897        this.block.css('position', 'relative').attr('aria-busy', this.cfg.blocked).append(this.blocker).append(this.content);
1898    
1899        if(this.block.length > 1) {
1900            this.blocker = $(PrimeFaces.escapeClientId(this.id + '_blocker'));
1901            this.content = this.block.children('.ui-blockui-content');
1902        }
1903    },
1904    
1905    hasContent: function() {
1906        return this.content.contents().length > 0;
1907    }
1908    
1909});
1910/**
1911 * PrimeFaces Calendar Widget
1912 */
1913PrimeFaces.widget.Calendar = PrimeFaces.widget.BaseWidget.extend({
1914
1915    init: function(cfg) {
1916        this._super(cfg);
1917
1918        this.input = $(this.jqId + '_input');
1919        this.jqEl = this.cfg.popup ? this.input : $(this.jqId + '_inline');
1920        var $this = this;
1921
1922        //i18n and l7n
1923        this.configureLocale();
1924
1925        //events
1926        this.bindDateSelectListener();
1927        this.bindViewChangeListener();
1928        this.bindCloseListener();
1929
1930        //disabled dates
1931        this.cfg.beforeShowDay = function(date) {
1932            if($this.cfg.preShowDay) {
1933                return $this.cfg.preShowDay(date);
1934            }
1935            else if($this.cfg.disabledWeekends) {
1936                return $.datepicker.noWeekends(date);
1937            }
1938            else {
1939                return [true,''];
1940            }
1941        }
1942
1943        //Setup timepicker
1944        var hasTimePicker = this.hasTimePicker();
1945        if(hasTimePicker) {
1946            this.configureTimePicker();
1947        }
1948
1949        //Client behaviors, input skinning and z-index
1950        if(this.cfg.popup) {
1951            PrimeFaces.skinInput(this.jqEl);
1952
1953            if(this.cfg.behaviors) {
1954                PrimeFaces.attachBehaviors(this.jqEl, this.cfg.behaviors);
1955            }
1956
1957            this.cfg.beforeShow = function(input, inst) {
1958                if($this.refocusInput) {
1959                    $this.refocusInput = false;
1960                    return false;
1961                }
1962
1963                //display on top
1964                setTimeout(function() {
1965                    $('#ui-datepicker-div').addClass('ui-input-overlay').css('z-index', ++PrimeFaces.zindex);
1966
1967                    if ($this.cfg.showTodayButton === false) {
1968                        $(input).datepicker("widget").find(".ui-datepicker-current").hide();
1969                    }
1970                    
1971                    $this.alignPanel();
1972                }, 1);
1973
1974                // touch support - prevents keyboard popup
1975                if(PrimeFaces.env.touch && !$this.input.attr("readonly") && $this.cfg.showOn && $this.cfg.showOn === 'button') {
1976                    $(this).prop("readonly", true);
1977                }
1978
1979                //user callback
1980                var preShow = $this.cfg.preShow;
1981                if(preShow) {
1982                    return $this.cfg.preShow.call($this, input, inst);
1983                }
1984            };
1985            
1986            PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $('#ui-datepicker-div'), function() {
1987                $this.alignPanel();
1988            });
1989        }
1990
1991        // touch support - prevents keyboard popup
1992        if (PrimeFaces.env.touch && !this.input.attr("readonly") && this.cfg.showOn && this.cfg.showOn === 'button') {
1993            var fireCloseEvent = this.cfg.onClose;
1994            this.cfg.onClose = function(dateText, inst) {
1995                $(this).attr("readonly", false);
1996                
1997                if (fireCloseEvent) {
1998                    fireCloseEvent();
1999                }
2000            };
2001        }
2002
2003        //Initialize calendar
2004        if(hasTimePicker) {
2005            if(this.cfg.timeOnly)
2006                this.jqEl.timepicker(this.cfg);
2007            else
2008                this.jqEl.datetimepicker(this.cfg);
2009        }
2010        else {
2011            this.jqEl.datepicker(this.cfg);
2012        }
2013
2014        //extensions
2015        if(this.cfg.popup && this.cfg.showOn) {
2016            var triggerButton = this.jqEl.siblings('.ui-datepicker-trigger:button');
2017            triggerButton.attr('aria-label',PrimeFaces.getAriaLabel('calendar.BUTTON')).attr('aria-haspopup', true).html('').addClass('ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only')
2018                        .append('<span class="ui-button-icon-left ui-icon ui-icon-calendar"></span><span class="ui-button-text">ui-button</span>');
2019
2020            var title = this.jqEl.attr('title');
2021            if(title) {
2022                triggerButton.attr('title', title);
2023            }
2024
2025            if(this.cfg.disabled) {
2026                triggerButton.addClass('ui-state-disabled');
2027            }
2028
2029            var buttonIndex = this.cfg.buttonTabindex||this.jqEl.attr('tabindex');
2030            if(buttonIndex) {
2031                triggerButton.attr('tabindex', buttonIndex);
2032            }
2033
2034            PrimeFaces.skinButton(triggerButton);
2035            $('#ui-datepicker-div').addClass('ui-shadow');
2036            this.jq.addClass('ui-trigger-calendar');
2037        }
2038
2039        //mark target and descandants of target as a trigger for a primefaces overlay
2040        if(this.cfg.popup) {
2041            this.jq.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
2042        }
2043
2044        if (!this.cfg.popup && this.cfg.showTodayButton === false) {
2045            this.jqEl.parent().find(".ui-datepicker-current").hide();
2046        }
2047
2048        //pfs metadata
2049        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
2050
2051        if (this.cfg.mask) {
2052            var maskCfg = {
2053                placeholder:this.cfg.maskSlotChar||'_',
2054                autoclear:this.cfg.maskAutoClear
2055            };
2056            this.input.mask(this.cfg.mask, maskCfg);
2057        }
2058    },
2059
2060    alignPanel: function () {
2061        if($.datepicker._lastInput && (this.id + '_input') === $.datepicker._lastInput.id) {
2062            $('#ui-datepicker-div').css({left: '', top: ''}).position({
2063                my: 'left top'
2064                , at: 'left bottom'
2065                , of: this.input
2066                , collision: 'flipfit'
2067            });
2068        }
2069    },
2070    
2071    refresh: function(cfg) {
2072        if(cfg.popup && $.datepicker._lastInput && (cfg.id + '_input') === $.datepicker._lastInput.id) {
2073            $.datepicker._hideDatepicker();
2074        }
2075
2076        this._super(cfg);
2077    },
2078
2079    configureLocale: function() {
2080        var localeSettings = PrimeFaces.locales[this.cfg.locale];
2081
2082        if(localeSettings) {
2083            for(var setting in localeSettings) {
2084                this.cfg[setting] = localeSettings[setting];
2085            }
2086        }
2087    },
2088
2089    bindDateSelectListener: function() {
2090        var $this = this;
2091
2092        this.cfg.onSelect = function() {
2093            if($this.cfg.popup) {
2094                $this.fireDateSelectEvent();
2095
2096                if($this.cfg.focusOnSelect) {
2097                    $this.refocusInput = true;
2098                    $this.jqEl.focus();
2099                    if(!($this.cfg.showOn && $this.cfg.showOn === 'button')) {
2100                        $this.jqEl.off('click.calendar').on('click.calendar', function() {
2101                            $(this).datepicker("show");
2102                        });
2103                    }
2104
2105                    setTimeout(function() {
2106                        $this.refocusInput = false;
2107                    }, 10);
2108                }
2109            }
2110            else {
2111                // GitHub #3760 Pass the config settings to TimePicker
2112                var settingsObj = {
2113                       settings : $this.cfg
2114                };
2115                var newDate = $this.cfg.timeOnly ? '' : $.datepicker.formatDate($this.cfg.dateFormat, $this.getDate(), $.datepicker._getFormatConfig(settingsObj));
2116                if($this.cfg.timeFormat) {
2117                   newDate += ' ' + $this.jqEl.find('.ui_tpicker_time_input')[0].value;
2118                }
2119
2120                $this.input.val(newDate);
2121                $this.fireDateSelectEvent();
2122            }
2123        };
2124    },
2125
2126    fireDateSelectEvent: function() {
2127        this.callBehavior('dateSelect');
2128    },
2129
2130    bindViewChangeListener: function() {
2131        if(this.hasBehavior('viewChange')) {
2132            var $this = this;
2133            this.cfg.onChangeMonthYear = function(year, month) {
2134                $this.fireViewChangeEvent(year, month);
2135            };
2136        }
2137    },
2138
2139    fireViewChangeEvent: function(year, month) {
2140        if(this.hasBehavior('viewChange')) {
2141            var ext = {
2142                    params: [
2143                        {name: this.id + '_month', value: month},
2144                        {name: this.id + '_year', value: year}
2145                    ]
2146            };
2147
2148            this.callBehavior('viewChange', ext);
2149        }
2150    },
2151
2152    bindCloseListener: function() {
2153        if(this.hasBehavior('close')) {
2154            var $this = this;
2155            this.cfg.onClose = function() {
2156                $this.fireCloseEvent();
2157            };
2158        }
2159    },
2160
2161    fireCloseEvent: function() {
2162        this.callBehavior('close');
2163    },
2164
2165    configureTimePicker: function() {
2166        var pattern = this.cfg.dateFormat,
2167        timeSeparatorIndex = pattern.toLowerCase().indexOf('h');
2168
2169        this.cfg.dateFormat = pattern.substring(0, timeSeparatorIndex - 1);
2170        this.cfg.timeFormat = pattern.substring(timeSeparatorIndex, pattern.length);
2171
2172        //ampm
2173        if(this.cfg.timeFormat.indexOf('TT') != -1) {
2174            this.cfg.ampm = true;
2175        }
2176        
2177        // GitHub #4366 pass date and time settings for min/max date
2178        var timeSettings = {
2179                settings : this.cfg
2180        };
2181        var parseSettings = $.datepicker._getFormatConfig(timeSettings);
2182
2183        //restraints
2184        if(this.cfg.minDate) {
2185            this.cfg.minDate = $.datepicker.parseDateTime(this.cfg.dateFormat, this.cfg.timeFormat, this.cfg.minDate, parseSettings, this.cfg);
2186        }
2187
2188        if(this.cfg.maxDate) {
2189            this.cfg.maxDate = $.datepicker.parseDateTime(this.cfg.dateFormat, this.cfg.timeFormat, this.cfg.maxDate, parseSettings, this.cfg);
2190        }
2191
2192        if(!this.cfg.showButtonPanel) {
2193            this.cfg.showButtonPanel = false;
2194        }
2195
2196        if(this.cfg.controlType == 'custom' && this.cfg.timeControlObject) {
2197            this.cfg.controlType = this.cfg.timeControlObject;
2198        }
2199
2200        if(this.cfg.showHour) {
2201            this.cfg.showHour = (this.cfg.showHour == "true") ? true : false;
2202        }
2203
2204        if(this.cfg.showMinute) {
2205            this.cfg.showMinute = (this.cfg.showMinute == "true") ? true : false;
2206        }
2207
2208        if(this.cfg.showSecond) {
2209            this.cfg.showSecond = (this.cfg.showSecond == "true") ? true : false;
2210        }
2211
2212        if(this.cfg.showMillisec) {
2213            this.cfg.showMillisec = (this.cfg.showMillisec == "true") ? true : false;
2214        }
2215    },
2216
2217    hasTimePicker: function() {
2218        return this.cfg.dateFormat.toLowerCase().indexOf('h') != -1;
2219    },
2220
2221    setDate: function(date) {
2222        this.jqEl.datetimepicker('setDate', date);
2223    },
2224
2225    getDate: function() {
2226        return this.jqEl.datetimepicker('getDate');
2227    },
2228
2229    enable: function() {
2230        this.jqEl.datetimepicker('enable');
2231    },
2232
2233    disable: function() {
2234        this.jqEl.datetimepicker('disable');
2235    }
2236
2237});
2238
2239/**
2240 * PrimeFaces Carousel Widget
2241 */
2242PrimeFaces.widget.Carousel = PrimeFaces.widget.DeferredWidget.extend({
2243
2244    init: function(cfg) {
2245        this._super(cfg);
2246        this.viewport = this.jq.children('.ui-carousel-viewport');
2247        this.itemsContainer = this.viewport.children('.ui-carousel-items');
2248        this.items = this.itemsContainer.children('li');
2249        this.itemsCount = this.items.length;
2250        this.header = this.jq.children('.ui-carousel-header');
2251        this.prevNav = this.header.children('.ui-carousel-prev-button');
2252        this.nextNav = this.header.children('.ui-carousel-next-button');
2253        this.pageLinks = this.header.find('> .ui-carousel-page-links > .ui-carousel-page-link');
2254        this.dropdown = this.header.children('.ui-carousel-dropdown');
2255        this.responsiveDropdown = this.header.children('.ui-carousel-dropdown-responsive');
2256        this.stateholder = $(this.jqId + '_page');
2257
2258        if(this.cfg.toggleable) {
2259            this.toggler = $(this.jqId + '_toggler');
2260            this.toggleStateHolder = $(this.jqId + '_collapsed');
2261            this.toggleableContent = this.jq.find(' > .ui-carousel-viewport > .ui-carousel-items, > .ui-carousel-footer');
2262        }
2263
2264        this.cfg.numVisible = this.cfg.numVisible||3;
2265        this.cfg.firstVisible = this.cfg.firstVisible||0;
2266        this.columns = this.cfg.numVisible;
2267        this.first = this.cfg.firstVisible;
2268        this.cfg.effectDuration = this.cfg.effectDuration||500;
2269        this.cfg.circular = this.cfg.circular||false;
2270        this.cfg.breakpoint = this.cfg.breakpoint||640;
2271        this.page = parseInt(this.first/this.columns);
2272        this.totalPages = Math.ceil(this.itemsCount/this.cfg.numVisible);
2273
2274        if(this.cfg.stateful) {
2275            this.stateKey = 'carousel-' + this.id;
2276
2277            this.restoreState();
2278        }
2279
2280        this.renderDeferred();
2281    },
2282
2283    _render: function() {
2284        this.updateNavigators();
2285        this.bindEvents();
2286
2287        if(this.cfg.vertical) {
2288            this.calculateItemHeights();
2289        }
2290        else if(this.cfg.responsive) {
2291            this.refreshDimensions();
2292        }
2293        else {
2294            this.calculateItemWidths(this.columns);
2295            this.jq.width(this.jq.width());
2296            this.updateNavigators();
2297        }
2298
2299        if(this.cfg.collapsed) {
2300            this.toggleableContent.hide();
2301        }
2302    },
2303
2304    calculateItemWidths: function() {
2305        var firstItem = this.items.eq(0);
2306        if(firstItem.length) {
2307            var itemFrameWidth = firstItem.outerWidth(true) - firstItem.width();    //sum of margin, border and padding
2308            this.items.width((this.viewport.innerWidth() - itemFrameWidth * this.columns) / this.columns);
2309        }
2310    },
2311
2312    calculateItemHeights: function() {
2313        var firstItem = this.items.eq(0);
2314        if(firstItem.length) {
2315            if(!this.cfg.responsive) {
2316                this.items.width(firstItem.width());
2317                this.jq.width(this.jq.width());
2318                var maxHeight = 0;
2319                for(var i = 0; i < this.items.length; i++) {
2320                    var item = this.items.eq(i),
2321                    height = item.height();
2322
2323                    if(maxHeight < height) {
2324                        maxHeight = height;
2325                    }
2326                }
2327                this.items.height(maxHeight);
2328            }
2329            var totalMargins = ((firstItem.outerHeight(true) - firstItem.outerHeight()) / 2) * (this.cfg.numVisible);
2330            this.viewport.height((firstItem.outerHeight() * this.cfg.numVisible) + totalMargins);
2331            this.updateNavigators();
2332            this.itemsContainer.css('top', -1 * (this.viewport.innerHe
2332ight() * this.page));
2333        }
2334    },
2335
2336    refreshDimensions: function() {
2337        var win = $(window);
2338        if(win.width() <= this.cfg.breakpoint) {
2339            this.columns = 1;
2340            this.calculateItemWidths(this.columns);
2341            this.totalPages = this.itemsCount;
2342            this.responsiveDropdown.show();
2343            this.pageLinks.hide();
2344        }
2345        else {
2346            this.columns = this.cfg.numVisible;
2347            this.calculateItemWidths();
2348            this.totalPages = Math.ceil(this.itemsCount / this.cfg.numVisible);
2349            this.responsiveDropdown.hide();
2350            this.pageLinks.show();
2351        }
2352
2353        this.page = parseInt(this.first / this.columns);
2354        this.updateNavigators();
2355        this.itemsContainer.css('left', (-1 * (this.viewport.innerWidth() * this.page)));
2356    },
2357
2358    bindEvents: function() {
2359        var $this = this;
2360
2361        this.prevNav.on('click', function() {
2362            if($this.page !== 0) {
2363                $this.setPage($this.page - 1);
2364            }
2365            else if($this.cfg.circular) {
2366                $this.setPage($this.totalPages - 1);
2367            }
2368        });
2369
2370        this.nextNav.on('click', function() {
2371            var lastPage = ($this.page === ($this.totalPages - 1));
2372
2373            if(!lastPage) {
2374                $this.setPage($this.page + 1);
2375            }
2376            else if($this.cfg.circular) {
2377                $this.setPage(0);
2378            }
2379        });
2380
2381        this.itemsContainer.swipe({
2382            swipeLeft:function(event) {
2383                if($this.page === ($this.totalPages - 1)) {
2384                    if($this.cfg.circular)
2385                        $this.setPage(0);
2386                }
2387                else {
2388                    $this.setPage($this.page + 1);
2389                }
2390            },
2391            swipeRight: function(event) {
2392            	if($this.page === 0) {
2393                    if($this.cfg.circular)
2394                        $this.setPage($this.totalPages - 1);
2395                }
2396                else {
2397                    $this.setPage($this.page - 1);
2398                }
2399            },
2400            excludedElements: PrimeFaces.utils.excludedSwipeElements()
2401        });
2402
2403        if(this.pageLinks.length) {
2404            this.pageLinks.on('click', function(e) {
2405                $this.setPage($(this).index());
2406                e.preventDefault();
2407            });
2408        }
2409
2410        this.header.children('select').on('change', function() {
2411            $this.setPage(parseInt($(this).val()) - 1);
2412        });
2413
2414        if(this.cfg.autoplayInterval) {
2415            this.cfg.circular = true;
2416            this.startAutoplay();
2417        }
2418
2419        if(this.cfg.responsive) {
2420            PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', null, function() {
2421                if($this.cfg.vertical) {
2422                    $this.calculateItemHeights();
2423                }
2424                else {
2425                    $this.refreshDimensions();
2426                }
2427            });
2428        }
2429
2430        if(this.cfg.toggleable) {
2431            this.toggler.on('mouseover.carouselToggler',function() {
2432                $(this).addClass('ui-state-hover');
2433            }).on('mouseout.carouselToggler',function() {
2434                $(this).removeClass('ui-state-hover');
2435            }).on('click.carouselToggler', function(e) {
2436                $this.toggle();
2437                e.preventDefault();
2438            });
2439        }
2440    },
2441
2442    updateNavigators: function() {
2443        if(!this.cfg.circular) {
2444            if(this.page === 0) {
2445                this.prevNav.addClass('ui-state-disabled');
2446                this.nextNav.removeClass('ui-state-disabled');
2447            }
2448            else if(this.page === (this.totalPages - 1)) {
2449                this.prevNav.removeClass('ui-state-disabled');
2450                this.nextNav.addClass('ui-state-disabled');
2451            }
2452            else {
2453                this.prevNav.removeClass('ui-state-disabled');
2454                this.nextNav.removeClass('ui-state-disabled');
2455            }
2456        }
2457
2458        if(this.pageLinks.length) {
2459            this.pageLinks.filter('.ui-icon-radio-on').removeClass('ui-icon-radio-on');
2460            this.pageLinks.eq(this.page).addClass('ui-icon-radio-on');
2461        }
2462
2463        if(this.dropdown.length) {
2464            this.dropdown.val(this.page + 1);
2465        }
2466
2467        if(this.responsiveDropdown.length) {
2468            this.responsiveDropdown.val(this.page + 1);
2469        }
2470    },
2471
2472    setPage: function(p) {
2473        if(p !== this.page && !this.itemsContainer.is(':animated')) {
2474            var $this = this,
2475            animationProps = this.cfg.vertical ? {top: -1 * (this.viewport.innerHe
2475ight() * p)} : {left: -1 * (this.viewport.innerWidth() * p)};
2476            animationProps.easing = this.cfg.easing;
2477
2478            this.itemsContainer.animate(animationProps,
2479            {
2480                duration: this.cfg.effectDuration,
2481                easing: this.cfg.easing,
2482                complete: function() {
2483                    $this.page = p;
2484                    $this.first = $this.page * $this.columns;
2485                    $this.updateNavigators();
2486                    $this.stateholder.val($this.page);
2487                    if($this.cfg.stateful) {
2488                        $this.saveState();
2489                    }
2490                }
2491            });
2492        }
2493    },
2494
2495    startAutoplay: function() {
2496        var $this = this;
2497
2498        this.interval = setInterval(function() {
2499            if($this.page === ($this.totalPages - 1))
2500                $this.setPage(0);
2501            else
2502                $this.setPage($this.page + 1);
2503        }, this.cfg.autoplayInterval);
2504    },
2505
2506    stopAutoplay: function() {
2507        clearInterval(this.interval);
2508    },
2509
2510    toggle: function() {
2511        if(this.cfg.collapsed) {
2512            this.expand();
2513        }
2514        else {
2515            this.collapse();
2516        }
2517
2518        PrimeFaces.invokeDeferredRenders(this.id);
2519    },
2520
2521    expand: function() {
2522        this.toggleState(false, 'ui-icon-plusthick', 'ui-icon-minusthick');
2523
2524        this.slideDown();
2525    },
2526
2527    collapse: function() {
2528        this.toggleState(true, 'ui-icon-minusthick', 'ui-icon-plusthick');
2529
2530        this.slideUp();
2531    },
2532
2533    slideUp: function() {
2534        this.toggleableContent.slideUp(this.cfg.toggleSpeed, 'easeInOutCirc');
2535    },
2536
2537    slideDown: function() {
2538        this.toggleableContent.slideDown(this.cfg.toggleSpeed, 'easeInOutCirc');
2539    },
2540
2541    toggleState: function(collapsed, removeIcon, addIcon) {
2542        this.toggler.children('span.ui-icon').removeClass(removeIcon).addClass(addIcon);
2543        this.cfg.collapsed = collapsed;
2544        this.toggleStateHolder.val(collapsed);
2545
2546        if(this.cfg.stateful) {
2547            this.saveState();
2548        }
2549    },
2550
2551    restoreState: function() {
2552        var carouselStateAsString = PrimeFaces.getCookie(this.stateKey) || "first: null, collapsed: null";
2553        this.carouselState = PrimeFaces.csp.evalResult('({' + carouselStateAsString + '})');
2554
2555        this.first = this.carouselState.first||this.first;
2556        this.page = parseInt(this.first/this.columns);
2557
2558        this.stateholder.val(this.page);
2559
2560        if(this.cfg.toggleable && (this.carouselState.collapsed === false || this.carouselState.collapsed === true)) {
2561            this.cfg.collapsed = !this.carouselState.collapsed;
2562            this.toggle();
2563        }
2564    },
2565
2566    saveState: function() {
2567        var carouselStateAsString = "first:" + this.first;
2568
2569        if(this.cfg.toggleable) {
2570            carouselStateAsString += ", collapsed: " + this.toggleStateHolder.val();
2571        }
2572
2573        PrimeFaces.setCookie(this.stateKey, carouselStateAsString, {path:'/'});
2574    },
2575
2576    clearState: function() {
2577        if(this.cfg.stateful) {
2578            PrimeFaces.deleteCookie(this.stateKey, {path:'/'});
2579        }
2580    }
2581
2582});
2583/**
2584 * PrimeFaces ColumnToggler Widget
2585 */
2586PrimeFaces.widget.ColumnToggler = PrimeFaces.widget.DeferredWidget.extend({
2587
2588    init: function(cfg) {
2589        this._super(cfg);
2590        this.table = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.cfg.datasource);
2591        this.trigger = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.cfg.trigger);
2592        this.tableId = this.table.attr('id');
2593        this.hasFrozenColumn = this.table.hasClass('ui-datatable-frozencolumn');
2594        this.hasStickyHeader = this.table.hasClass('ui-datatable-sticky');
2595        var clientId = PrimeFaces.escapeClientId(this.tableId);
2596
2597        if(this.hasFrozenColumn) {
2598            this.thead = $(clientId + '_frozenThead,' + clientId + '_scrollableThead');
2599            this.tbody = $(clientId + '_frozenTbody,' + clientId + '_scrollableTbody');
2600            this.tfoot = $(clientId + '_frozenTfoot,' + clientId + '_scrollableTfoot');
2601            this.frozenColumnCount = this.thead.eq(0).find('th').length;
2602        }
2603        else {
2604            this.thead = $(clientId + '_head');
2605            this.tbody = $(clientId + '_data');
2606            this.tfoot = $(clientId + '_foot');
2607        }
2608        this.visible = false;
2609
2610        this.render();
2611        this.bindEvents();
2612    },
2613
2614    //@override
2615    refresh: function(cfg) {
2616        var jqs = $('[id=' + cfg.id.replace(/:/g,"\\:") + ']');
2617        if(jqs.length > 1) {
2618            $(document.body).children(this.jqId).remove();
2619        }
2620
2621        this.widthAligned = false;
2622
2623        this._super(cfg);
2624    },
2625
2626    render: function() {
2627        this.columns = this.thead.find('> tr > th:not(.ui-static-column)');
2628        this.panel = $(PrimeFaces.escapeClientId(this.cfg.id)).attr('role', 'dialog').addClass('ui-columntoggler ui-widget ui-widget-content ui-shadow ui-corner-all')
2629                .append('<ul class="ui-columntoggler-items" role="group"></ul>').appendTo(document.body);
2630        this.itemContainer = this.panel.children('ul');
2631
2632        var stateHolderId = this.tableId + "_columnTogglerState";
2633        this.togglerStateHolder = $('<input type="hidden" id="' + stateHolderId + '" name="' + stateHolderId + '" autocomplete="off"></input>');
2634        this.table.append(this.togglerStateHolder);
2635        this.togglerState = [];
2636
2637        //items
2638        for(var i = 0; i < this.columns.length; i++) {
2639            var column = this.columns.eq(i),
2640            hidden = column.hasClass('ui-helper-hidden'),
2641            boxClass = hidden ? 'ui-chkbox-box ui-widget ui-corner-all ui-state-default' : 'ui-chkbox-box ui-widget ui-corner-all ui-state-default ui-state-active',
2642            iconClass = (hidden) ? 'ui-chkbox-icon ui-icon ui-icon-blank' : 'ui-chkbox-icon ui-icon ui-icon-check',
2643            columnChildren = column.children('.ui-column-title'),
2644            columnTitle = columnChildren.text(),
2645            columnTogglerCheckboxId = this.tableId + "_columnTogglerChkbx" + i;
2646            
2647            var label = columnChildren.find('label');
2648            if (label.length) {
2649                columnTitle = label.text();
2650            }
2651
2652            this.hasPriorityColumns = column.is('[class*="ui-column-p-"]');
2653
2654            var item = $('<li class="ui-columntoggler-item">' +
2655                    '<div class="ui-chkbox ui-widget">' +
2656                    '<div role="checkbox" tabindex="0" aria-checked="'+ !hidden + '" aria-labelledby="'+ columnTogglerCheckboxId + '" class="' + boxClass + '">' +
2657                    '<span class="' + iconClass + '"></span></div></div>' +
2658                    '<label id="' + columnTogglerCheckboxId + '">' + PrimeFaces.escapeHTML(columnTitle) + '</label></li>').data('column', column.attr('id'));
2659
2660            if(this.hasPriorityColumns) {
2661                var columnClasses = column.attr('class').split(' ');
2662                for(var j = 0; j < columnClasses.length; j++) {
2663                    var columnClass = columnClasses[j],
2664                    pindex = columnClass.indexOf('ui-column-p-');
2665                    if(pindex !== -1) {
2666                        item.addClass(columnClass.substring(pindex , pindex + 13));
2667                    }
2668                }
2669            }
2670
2671            item.appendTo(this.itemContainer);
2672
2673            this.togglerState.push(column.attr('id') + '_' + !hidden);
2674        }
2675
2676        this.togglerStateHolder.val(this.togglerState.join(','));
2677
2678        //close icon
2679        this.closer = $('<a href="#" class="ui-columntoggler-close"><span class="ui-icon ui-icon-close"></span></a>')
2680                .attr('aria-label', PrimeFaces.getAriaLabel('columntoggler.CLOSE')).prependTo(this.panel);
2681
2682        if(this.panel.outerHeight() > 200) {
2683            this.panel.height(200);
2684        }
2685        this.hide();
2686    },
2687
2688    bindEvents: function() {
2689        var $this = this;
2690
2691        //trigger
2692        this.trigger.off('click.ui-columntoggler').on('click.ui-columntoggler', function(e) {
2693            if($this.visible)
2694                $this.hide();
2695            else
2696                $this.show();
2697        });
2698
2699        //checkboxes
2700        this.itemContainer.find('> .ui-columntoggler-item > .ui-chkbox > .ui-chkbox-box').on('mouseover.columnToggler', function() {
2701                var item = $(this);
2702                if(!item.hasClass('ui-state-active')) {
2703                    item.addClass('ui-state-hover');
2704                }
2705            })
2706            .on('mouseout.columnToggler', function() {
2707                $(this).removeClass('ui-state-hover');
2708            })
2709            .on('click.columnToggler', function(e) {
2710                $this.toggle($(this));
2711                e.preventDefault();
2712            });
2713
2714        //labels
2715        this.itemContainer.find('> .ui-columntoggler-item > label').on('click.selectCheckboxMenu', function(e) {
2716            $this.toggle($(this).prev().children('.ui-chkbox-box'));
2717            PrimeFaces.clearSelection();
2718            e.preventDefault();
2719        });
2720
2721        //closer
2722        this.closer.on('click', function(e) {
2723            $this.hide();
2724            $this.trigger.focus();
2725            e.preventDefault();
2726        });
2727
2728        this.bindKeyEvents();
2729        
2730        PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.panel,
2731            function() { return $this.trigger; },
2732            function(e, eventTarget) {
2733                if(!($this.panel.is(eventTarget) || $this.panel.has(eventTarget).length > 0)) {
2734                    $this.hide();
2735                }
2736            });
2737
2738        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.panel, function() {
2739            $this.alignPanel();
2740        });
2741    },
2742
2743    bindKeyEvents: function() {
2744        var $this = this,
2745        inputs = this.itemContainer.find('> li > div.ui-chkbox > div.ui-chkbox-box');
2746
2747        this.trigger.on('focus.columnToggler', function() {
2748            $(this).addClass('ui-state-focus');
2749        })
2750        .on('blur.columnToggler', function() {
2751            $(this).removeClass('ui-state-focus');
2752        })
2753        .on('keydown.columnToggler', function(e) {
2754            var keyCode = $.ui.keyCode,
2755            key = e.which;
2756
2757            switch(key) {
2758                case keyCode.ENTER:
2759                    if($this.visible)
2760                        $this.hide();
2761                    else
2762                        $this.show();
2763
2764                    e.preventDefault();
2765                break;
2766
2767                case keyCode.TAB:
2768                    if($this.visible) {
2769                        $this.itemContainer.children('li:not(.ui-state-disabled):first').find('div.ui-chkbox-box').trigger('focus');
2770                        e.preventDefault();
2771                    }
2772                break;
2773            };
2774        });
2775
2776        inputs.on('focus.columnToggler', function() {
2777            $(this).addClass('ui-state-focus');
2778            //PrimeFaces.scrollInView($this.panel, box);
2779        })
2780        .on('blur.columnToggler', function(e) {
2781            $(this).removeClass('ui-state-focus');
2782        })
2783        .on('keydown.columnToggler', function(e) {
2784            var keyCode = $.ui.keyCode,
2785            key = e.which;
2786
2787            switch(key) {
2788                case keyCode.TAB:
2789                    var index = $(this).closest('li').index();
2790                    if(e.shiftKey) {
2791                        if(index === 0)
2792                            $this.closer.focus();
2793                        else
2794                            inputs.eq(index - 1).focus();
2795                    }
2796                    else {
2797                        if(index === ($this.columns.length - 1) && !e.shiftKey)
2798                            $this.closer.focus();
2799                        else
2800                            inputs.eq(index + 1).focus();
2801                    }
2802
2803                    e.preventDefault();
2804                    break;
2805                case keyCode.ENTER:
2806                case keyCode.SPACE:
2807                    $this.toggle($(this));
2808                    e.preventDefault();
2809                    break;
2810            }
2811        })
2812        .on('change.columnToggler', function(e) {
2813            if($(this).attr('aria-checked')) {
2814                $this.check(box);
2815                $(this).removeClass('ui-state-active');
2816            }
2817            else {
2818                $this.uncheck(box);
2819            }
2820        });
2821
2822        this.closer.on('keydown.columnToggler', function(e) {
2823            var key = e.which,
2824            keyCode = $.ui.keyCode;
2825
2826            if((key === keyCode.ENTER)) {
2827                $this.hide();
2828                $this.trigger.focus();
2829                e.preventDefault();
2830            }
2831            else if(key === keyCode.TAB) {
2832                if(e.shiftKey)
2833                    inputs.eq($this.columns.length - 1).focus();
2834                else
2835                    inputs.eq(0).focus();
2836
2837                e.preventDefault();
2838            }
2839        });
2840    },
2841
2842    toggle: function(chkbox) {
2843        if(chkbox.hasClass('ui-state-active')) {
2844            this.uncheck(chkbox);
2845        }
2846        else {
2847            this.check(chkbox);
2848        }
2849    },
2850
2851    check: function(chkbox) {
2852        chkbox.addClass('ui-state-active').removeClass('ui-state-hover').children('.ui-chkbox-icon').addClass('ui-icon-check').removeClass('ui-icon-blank');
2853
2854        var column = $(document.getElementById(chkbox.closest('li.ui-columntoggler-item').data('column'))),
2855        index = column.index() + 1,
2856        thead = this.hasFrozenColumn ? (column.hasClass('ui-frozen-column') ? this.thead.eq(0) : this.thead.eq(1)) : this.thead,
2857        tbody = this.hasFrozenColumn ? (column.hasClass('ui-frozen-column') ? this.tbody.eq(0) : this.tbody.eq(1)) : this.tbody,
2858        tfoot = this.hasFrozenColumn ? (column.hasClass('ui-frozen-column') ? this.tfoot.eq(0) : this.tfoot.eq(1)) : this.tfoot;
2859
2860        var rowHeader = thead.children('tr'),
2861        columnHeader = rowHeader.find('th:nth-child(' + index + ')');
2862
2863        chkbox.attr('aria-checked', true);
2864        columnHeader.removeClass('ui-helper-hidden');
2865        $(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).removeClass('ui-helper-hidden');
2866        tbody.children('tr').find('td:nth-child(' + index + ')').removeClass('ui-helper-hidden');
2867        tfoot.children('tr').find('td:nth-child(' + index + ')').removeClass('ui-helper-hidden');
2868
2869        if(this.hasFrozenColumn) {
2870            var headers = rowHeader.children('th');
2871            if(headers.length !== headers.filter('.ui-helper-hidden').length) {
2872                thead.closest('td').removeClass('ui-helper-hidden');
2873            }
2874
2875            if(!column.hasClass('ui-frozen-column')) {
2876                index += this.frozenColumnCount;
2877            }
2878        }
2879
2880        if(this.hasStickyHeader) {
2881            $(PrimeFaces.escapeClientId(columnHeader.attr('id'))).removeClass('ui-helper-hidden');
2882        }
2883
2884        this.changeTogglerState(column, true);
2885        this.fireToggleEvent(true, (index - 1));
2886        this.updateColspan();
2887    },
2888
2889    uncheck: function(chkbox) {
2890        chkbox.removeClass('ui-state-active').children('.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
2891
2892        var column = $(document.getElementById(chkbox.closest('li.ui-columntoggler-item').data('column'))),
2893        index = column.index() + 1,
2894        thead = this.hasFrozenColumn ? (column.hasClass('ui-frozen-column') ? this.thead.eq(0) : this.thead.eq(1)) : this.thead,
2895        tbody = this.hasFrozenColumn ? (column.hasClass('ui-frozen-column') ? this.tbody.eq(0) : this.tbody.eq(1)) : this.tbody,
2896        tfoot = this.hasFrozenColumn ? (column.hasClass('ui-frozen-column') ? this.tfoot.eq(0) : this.tfoot.eq(1)) : this.tfoot;
2897
2898        var rowHeader = thead.children('tr'),
2899        columnHeader = rowHeader.find('th:nth-child(' + index + ')');
2900
2901        chkbox.attr('aria-checked', false);
2902        columnHeader.addClass('ui-helper-hidden');
2903        $(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).addClass('ui-helper-hidden');
2904        tbody.children('tr').find('td:nth-child(' + index + ')').addClass('ui-helper-hidden');
2905        tfoot.children('tr').find('td:nth-child(' + index + ')').addClass('ui-helper-hidden');
2906
2907        if(this.hasFrozenColumn) {
2908            var headers = rowHeader.children('th');
2909            if(headers.length === headers.filter(':hidden').length) {
2910                thead.closest('td').addClass('ui-helper-hidden');
2911            }
2912
2913            if(!column.hasClass('ui-frozen-column')) {
2914                index += this.frozenColumnCount;
2915            }
2916        }
2917
2918        if(this.hasStickyHeader) {
2919            $(PrimeFaces.escapeClientId(columnHeader.attr('id'))).addClass('ui-helper-hidden');
2920        }
2921
2922        this.changeTogglerState(column, false);
2923        this.fireToggleEvent(false, (index - 1));
2924        this.updateColspan();
2925    },
2926
2927    alignPanel: function() {
2928        this.panel.css({'left':'', 'top':'', 'z-index': ++PrimeFaces.zindex}).position({
2929                            my: 'left top'
2930                            ,at: 'left bottom'
2931                            ,of: this.trigger
2932                        });
2933
2934        if(this.hasPriorityColumns) {
2935            if(this.panel.outerWidth() <= this.trigger.outerWidth()) {
2936                this.panel.css('width','auto');
2937            }
2938
2939            this.widthAligned = false;
2940        }
2941
2942        if(!this.widthAligned && (this.panel.outerWidth() < this.trigger.outerWidth())) {
2943            this.panel.width(this.trigger.width());
2944            this.widthAligned = true;
2945        }
2946    },
2947
2948    show: function() {
2949        this.alignPanel();
2950        this.panel.show();
2951        this.visible = true;
2952        this.trigger.attr('aria-expanded', true);
2953        this.closer.trigger('focus');
2954    },
2955
2956    hide: function() {
2957        this.panel.fadeOut('fast');
2958        this.visible = false;
2959        this.trigger.attr('aria-expanded', false);
2960    },
2961
2962    fireToggleEvent: function(visible, index) {
2963        if(this.hasBehavior('toggle')) {
2964            var ext = {
2965                params: [
2966                    {name: this.id + '_visibility', value: visible ? 'VISIBLE' : 'HIDDEN'},
2967                    {name: this.id + '_index', value: index}
2968                ]
2969            };
2970
2971            this.callBehavior('toggle', ext);
2972        }
2973    },
2974
2975    calculateColspan: function() {
2976        return this.itemContainer.find('> .ui-columntoggler-item > .ui-chkbox > .ui-chkbox-box.ui-state-active').length;
2977    },
2978
2979    updateRowColspan: function(row, colspanValue) {
2980        colspanValue = colspanValue || this.calculateColspan();
2981        if(colspanValue) {
2982            row.children('td').removeClass('ui-helper-hidden').attr('colspan', colspanValue);
2983        }
2984        else {
2985            row.children('td').addClass('ui-helper-hidden');
2986        }
2987    },
2988
2989    updateColspan: function() {
2990        var emptyRow = this.tbody.children('tr:first');
2991        if(emptyRow && emptyRow.hasClass('ui-datatable-empty-message')) {
2992            this.updateRowColspan(emptyRow);
2993        }
2994        else {
2995            var colspanValue = this.calculateColspan(),
2996                $this = this;
2997            this.tbody.children('.ui-expanded-row-content').each(function() {
2998                $this.updateRowColspan($(this), colspanValue);
2999            });
3000        }
3001    },
3002
3003    changeTogglerState: function(column, isHidden) {
3004        if(column && column.length) {
3005            var stateVal = this.togglerStateHolder.val(),
3006            columnId = column.attr('id'),
3007            oldColState = columnId + "_" + !isHidden,
3008            newColState = columnId + "_" + isHidden;
3009            this.togglerStateHolder.val(stateVal.replace(oldColState, newColState));
3010        }
3011    }
3012
3013});
3014
3015/**
3016 * PrimeFaces Dashboard Widget
3017 */
3018PrimeFaces.widget.Dashboard = PrimeFaces.widget.BaseWidget.extend({
3019
3020    init: function(cfg) {
3021        this._super(cfg);
3022
3023        this.cfg.connectWith =  this.jqId + ' .ui-dashboard-column';
3024        this.cfg.placeholder = 'ui-state-hover';
3025        this.cfg.forcePlaceholderSize = true;
3026        this.cfg.revert=false;
3027        this.cfg.handle='.ui-panel-titlebar';
3028
3029        var $this = this;
3030
3031        if(this.hasBehavior('reorder')) {
3032            this.cfg.update = function(e, ui) {
3033                if(this === ui.item.parent()[0]) {
3034                    var itemIndex = ui.item.parent().children().filter(':not(script):visible').index(ui.item),
3035                    receiverColumnIndex =  ui.item.parent().parent().children().index(ui.item.parent());
3036
3037                    var ext = {
3038                        params: [
3039                            {name: $this.id + '_reordered', value: true},
3040                            {name: $this.id + '_widgetId', value: ui.item.attr('id')},
3041                            {name: $this.id + '_itemIndex', value: itemIndex},
3042                            {name: $this.id + '_receiverColumnIndex', value: receiverColumnIndex}
3043                        ]
3044                    };
3045
3046                    if(ui.sender) {
3047                        ext.params.push({name: $this.id + '_senderColumnIndex', value: ui.sender.parent().children().index(ui.sender)});
3048                    }
3049
3050                    $this.callBehavior('reorder', ext);
3051                }
3052            };
3053        }
3054
3055        $(this.jqId + ' .ui-dashboard-column').sortable(this.cfg);
3056    }
3057
3058});
3059/**
3060 * PrimeFaces DataGrid Widget
3061 */
3062PrimeFaces.widget.DataGrid = PrimeFaces.widget.BaseWidget.extend({
3063
3064    init: function(cfg) {
3065        this._super(cfg);
3066
3067        this.cfg.formId = $(this.jqId).closest('form').attr('id');
3068        this.content = $(this.jqId + '_content');
3069
3070        if(this.cfg.paginator) {
3071            this.setupPaginator();
3072        }
3073    },
3074
3075    setupPaginator: function() {
3076        var $this = this;
3077        this.cfg.paginator.paginate = function(newState) {
3078            $this.handlePagination(newState);
3079        };
3080
3081        this.paginator = new PrimeFaces.widget.Paginator(this.cfg.paginator);
3082    },
3083
3084    handlePagination: function(newState) {
3085        var $this = this,
3086        options = {
3087            source: this.id,
3088            update: this.id,
3089            process: this.id,
3090            formId: this.cfg.formId,
3091            params: [
3092                {name: this.id + '_pagination', value: true},
3093                {name: this.id + '_skipChildren', value: true},
3094                {name: this.id + '_first', value: newState.first},
3095                {name: this.id + '_rows', value: newState.rows}
3096            ],
3097            onsuccess: function(responseXML, status, xhr) {
3098                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
3099                        widget: $this,
3100                        handle: function(content) {
3101                            this.content.html(content);
3102                        }
3103                    });
3104
3105                return true;
3106            },
3107            oncomplete: function() {
3108                $this.paginator.cfg.page = newState.page;
3109                $this.paginator.updateUI();
3110            }
3111        };
3112
3113        if(this.hasBehavior('page')) {
3114            this.callBehavior('page', options);
3115        }
3116        else {
3117            PrimeFaces.ajax.Request.handle(options);
3118        }
3119    },
3120
3121    getPaginator: function() {
3122        return this.paginator;
3123    }
3124
3125});
3126/**
3127 * PrimeFaces DataList Widget
3128 */
3129PrimeFaces.widget.DataList = PrimeFaces.widget.BaseWidget.extend({
3130
3131    init: function(cfg) {
3132        this._super(cfg);
3133
3134        this.cfg.formId = $(this.jqId).parents('form:first').attr('id');
3135        this.content = $(this.jqId + '_content');
3136
3137        if(this.cfg.paginator) {
3138            this.setupPaginator();
3139        }
3140    },
3141
3142    setupPaginator: function() {
3143        var $this = this;
3144        this.cfg.paginator.paginate = function(newState) {
3145            $this.handlePagination(newState);
3146        };
3147
3148        this.paginator = new PrimeFaces.widget.Paginator(this.cfg.paginator);
3149    },
3150
3151    handlePagination: function(newState) {
3152        var $this = this,
3153        options = {
3154            source: this.id,
3155            update: this.id,
3156            process: this.id,
3157            formId: this.cfg.formId,
3158            params: [
3159                {name: this.id + '_pagination', value: true},
3160                {name: this.id + '_skipChildren', value: true},
3161                {name: this.id + '_first', value: newState.first},
3162                {name: this.id + '_rows', value: newState.rows}
3163            ],
3164            onsuccess: function(responseXML, status, xhr) {
3165                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
3166                        widget: $this,
3167                        handle: function(content) {
3168                            this.content.html(content);
3169                        }
3170                    });
3171
3172                return true;
3173            },
3174            oncomplete: function() {
3175                $this.paginator.cfg.page = newState.page;
3176                $this.paginator.updateUI();
3177            }
3178        };
3179
3180        if(this.hasBehavior('page')) {
3181            this.callBehavior('page', options);
3182        }
3183        else {
3184            PrimeFaces.ajax.Request.handle(options);
3185        }
3186    },
3187
3188    getPaginator: function() {
3189        return this.paginator;
3190    }
3191
3192});
3193/**
3194 * PrimeFaces DataScroller Widget
3195 */
3196PrimeFaces.widget.DataScroller = PrimeFaces.widget.BaseWidget.extend({
3197
3198    init: function(cfg) {
3199        this._super(cfg);
3200        this.content = this.jq.children('div.ui-datascroller-content');
3201        this.list = this.cfg.virtualScroll ? this.content.children('div').children('ul') : this.content.children('ul');
3202        this.loaderContainer = this.content.children('div.ui-datascroller-loader');
3203        this.loadStatus = $('<div class="ui-datascroller-loading"></div>');
3204        this.loading = false;
3205        this.allLoaded = false;
3206        this.cfg.offset = 0;
3207        this.cfg.mode = this.cfg.mode||'document';
3208        this.cfg.buffer = (100 - this.cfg.buffer) / 100;
3209
3210        if(this.cfg.loadEvent === 'scroll') {
3211            this.bindScrollListener();
3212        }
3213        else {
3214            this.loadTrigger = this.loaderContainer.children();
3215            this.bindManualLoader();
3216        }
3217    },
3218
3219    bindScrollListener: function() {
3220        var $this = this;
3221
3222        if(this.cfg.mode === 'document') {
3223            var win = $(window),
3224            doc = $(document),
3225            $this = this;
3226
3227            PrimeFaces.utils.registerScrollHandler(this, 'scroll.' + this.id + '_align', function() {
3228                if (win.scrollTop() >= ((doc.height() * $this.cfg.buffer) - win.height()) && $this.shouldLoad()) {
3229                    $this.load();
3230                }
3231            });
3232        }
3233        else {
3234            this.itemHeight = 0;
3235
3236            if(this.cfg.virtualScroll) {
3237                var item = this.list.children('li.ui-datascroller-item');
3238                if(item) {
3239                    this.itemHeight = item.outerHeight();
3240                    this.content.children('div').css('min-height', parseFloat((this.cfg.totalSize * this.itemHeight) + 'px'));
3241                }
3242
3243                if (this.cfg.startAtBottom) {
3244                    var pageHeight = this.itemHeight * this.cfg.chunkSize,
3245                    virtualListHeight = parseFloat(this.cfg.totalSize * this.itemHeight),
3246                    viewportHeight = this.content.height(),
3247                    pageCount = Math.floor(virtualListHeight / pageHeight)||1,
3248                    page = (this.cfg.totalSize % this.cfg.chunkSize) == 0 ? pageCount - 2 : pageCount - 1,
3249                    top = (virtualListHeight < viewportHeight) ? (viewportHeight - virtualListHeight) : (Math.max(page, 0) * pageHeight);
3250
3251                    this.list.css('top', top);
3252                    this.content.scrollTop(this.content[0].scrollHeight);
3253                }
3254            }
3255            else if (this.cfg.startAtBottom) {                
3256                this.content.scrollTop(this.content[0].scrollHeight);
3257                this.cfg.offset = this.cfg.totalSize > this.cfg.chunkSize ? this.cfg.totalSize - this.cfg.chunkSize : this.cfg.totalSize;
3258                
3259                var paddingTop = '';
3260                if (this.content.height() > this.list.height()) {
3261                    paddingTop = (this.getInnerContentHeight() - this.list.outerHeight() - this.loaderContainer.outerHeight());
3262                }
3263                
3264                this.list.css('padding-top', paddingTop);
3265            }
3266            
3267            this.content.on('scroll', function () {
3268                if($this.cfg.virtualScroll) {                    
3269                    var virtualScrollContent = this;
3270                    
3271                    clearTimeout($this.scrollTimeout);
3272                    $this.scrollTimeout = setTimeout(function() {
3273                        var viewportHeight = $this.content.outerHeight(),
3274                        listHeight = $this.list.outerHeight() + Math.ceil(viewportHeight - $this.content.height()),
3275                        pageHeight = $this.itemHeight * $this.cfg.chunkSize,
3276                        virtualListHeight = parseFloat($this.cfg.totalSize * $this.itemHeight),
3277                        pageCount = (virtualListHeight / pageHeight)||1;
3278
3279                        if(virtualScrollContent.scrollTop + viewportHeight > parseFloat($this.list.css('top')) + listHeight || virtualScrollContent.scrollTop < parseFloat($this.list.css('top'))) {
3280                            var page = Math.floor((virtualScrollContent.scrollTop * pageCount) / (virtualScrollContent.scrollHeight)) + 1;
3281                            $this.loadRowsWithVirtualScroll(page, function () {
3282                                $this.list.css('top', ((page - 1) * pageHeight) + 'px');
3283                            });
3284                        }
3285                    }, 200);
3286                }
3287                else {                    
3288                    var scrollTop = this.scrollTop,
3289                    scrollHeight = this.scrollHeight,
3290                    viewportHeight = this.clientHeight,
3291                    shouldLoad = $this.shouldLoad() && ($this.cfg.startAtBottom ?
3292                                (scrollTop <= (scrollHeight - (scrollHeight * $this.cfg.buffer))) && ($this.cfg.totalSize > $this.cfg.chunkSize)
3293                                :
3294                                (scrollTop >= ((scrollHeight * $this.cfg.buffer) - viewportHeight)));
3295                    if (shouldLoad) {
3296                        $this.load();
3297                    }
3298                }
3299            });
3300        }
3301    },
3302
3303    loadRowsWithVirtualScroll: function(page, callback) {
3304        if(this.virtualScrollActive) {
3305            return;
3306        }
3307
3308        this.virtualScrollActive = true;
3309
3310        var $this = this,
3311        first = (page - 1) * this.cfg.chunkSize,
3312        options = {
3313            source: this.id,
3314            process: this.id,
3315            update: this.id,
3316            formId: this.cfg.formId,
3317            params: [{name: this.id + '_virtualScrolling', value: true},
3318                     {name: this.id + '_first', value: first}],
3319            onsuccess: function(responseXML, status, xhr) {
3320                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
3321                    widget: $this,
3322                    handle: function(content) {
3323                        //insert new rows
3324                        this.updateData(content);
3325                        callback();
3326                        this.virtualScrollActive = false;
3327                    }
3328                });
3329
3330                return true;
3331            },
3332            oncomplete: function(xhr, status, args) {
3333                if(typeof args.totalSize !== 'undefined') {
3334                    $this.cfg.totalSize = args.totalSize;
3335                }
3336            }
3337        };
3338
3339        PrimeFaces.ajax.Request.handle(options);
3340    },
3341    
3342    updateData: function(data, clear, pre) {
3343        var empty = (clear === undefined) ? true: clear;
3344
3345        if(empty)
3346            this.list.html(data);
3347        else if (pre)
3348            this.list.prepend(data);
3349        else
3350            this.list.append(data);
3351    },
3352    
3353    bindManualLoader: function() {
3354        var $this = this;
3355
3356        this.loadTrigger.on('click.dataScroller', function(e) {
3357            $this.load();
3358            e.preventDefault();
3359        });
3360    },
3361
3362    load: function() {
3363        this.loading = true;
3364        this.cfg.offset += (this.cfg.chunkSize * (this.cfg.startAtBottom ? -1 : 1));
3365
3366        this.loadStatus.appendTo(this.loaderContainer);
3367        if(this.loadTrigger) {
3368            this.loadTrigger.hide();
3369        }
3370
3371        var $this = this,
3372        options = {
3373            source: this.id,
3374            process: this.id,
3375            update: this.id,
3376            global: false,
3377            params: [{name: this.id + '_load', value: true},{name: this.id + '_offset', value: this.cfg.offset}],
3378            onsuccess: function(responseXML, status, xhr) {
3379                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
3380                    widget: $this,
3381                    handle: function(content) {
3382                        this.updateData(content, false, $this.cfg.startAtBottom);
3383                    }
3384                });
3385
3386                return true;
3387            },
3388            oncomplete: function() {
3389                if ($this.cfg.offset < 0) {
3390                    $this.cfg.offset = 0;
3391                }
3392
3393                $this.loading = false;
3394                $this.allLoaded = ($this.cfg.startAtBottom) ? $this.cfg.offset == 0 : ($this.cfg.offset + $this.cfg.chunkSize) >= $this.cfg.totalSize;
3395
3396                $this.loadStatus.remove();
3397
3398                if($this.loadTrigger && !$this.allLoaded) {
3399                    $this.loadTrigger.show();
3400                }
3401            }
3402        };
3403
3404        PrimeFaces.ajax.Request.handle(options);
3405    },
3406
3407    shouldLoad: function() {
3408        return (!this.loading && !this.allLoaded);
3409    },
3410            
3411    getInnerContentHeight: function() {
3412        return (this.content.innerHeight() - parseFloat(this.content.css('padding-top')) - parseFloat(this.content.css('padding-bottom')));
3413    }
3414    
3415});
3416/**
3417 * PrimeFaces DataTable Widget
3418 */
3419PrimeFaces.widget.DataTable = PrimeFaces.widget.DeferredWidget.extend({
3420
3421    SORT_ORDER: {
3422        ASCENDING: 1,
3423        DESCENDING: -1,
3424        UNSORTED: 0
3425    },
3426
3427    init: function(cfg) {
3428        this._super(cfg);
3429
3430        this.thead = this.getThead();
3431        this.tbody = this.getTbody();
3432        this.tfoot = this.getTfoot();
3433
3434        if(this.cfg.paginator) {
3435            this.bindPaginator();
3436        }
3437
3438        this.bindSortEvents();
3439
3440        if(this.cfg.rowHover) {
3441            this.setupRowHover();
3442        }
3443
3444        if(this.cfg.selectionMode) {
3445            this.setupSelection();
3446        }
3447
3448        if(this.cfg.filter) {
3449            this.setupFiltering();
3450        }
3451
3452        if(this.cfg.expansion) {
3453            this.expansionProcess = [];
3454            this.bindExpansionEvents();
3455        }
3456
3457        if(this.cfg.editable) {
3458            this.bindEditEvents();
3459        }
3460
3461        if(this.cfg.draggableRows) {
3462            this.makeRowsDraggable();
3463        }
3464
3465        if(this.cfg.reflow) {
3466            this.initReflow();
3467        }
3468
3469        if(this.cfg.multiViewState && this.cfg.resizableColumns) {
3470            this.resizableStateHolder = $(this.jqId + '_resizableColumnState');
3471            this.resizableState = [];
3472
3473            if(this.resizableStateHolder.attr('value')) {
3474                this.resizableState = this.resizableStateHolder.val().split(',');
3475            }
3476        }
3477
3478        this.updateEmptyColspan();
3479        this.renderDeferred();
3480    },
3481
3482    _render: function() {
3483        this.isRTL = this.jq.hasClass('ui-datatable-rtl');
3484        this.cfg.partialUpdate = (this.cfg.partialUpdate === false) ? false : true;
3485        
3486        if(this.cfg.scrollable) {
3487            this.setupScrolling();
3488        }
3489
3490        if(this.cfg.groupColumnIndexes) {
3491            this.groupRows();
3492            this.bindToggleRowGroupEvents();
3493        }
3494
3495        if(this.cfg.resizableColumns) {
3496            this.setupResizableColumns();
3497        }
3498
3499        if(this.cfg.draggableColumns) {
3500            this.setupDraggableColumns();
3501        }
3502
3503        if(this.cfg.stickyHeader) {
3504            this.setupStickyHeader();
3505        }
3506
3507        if(this.cfg.onRowClick) {
3508            this.bindRowClick();
3509        }
3510
3511        if(this.cfg.expansion) {
3512            this.updateExpandedRowsColspan();
3513        }
3514    },
3515
3516    getThead: function() {
3517        return $(this.jqId + '_head');
3518    },
3519
3520    getTbody: function() {
3521        return $(this.jqId + '_data');
3522    },
3523
3524    getTfoot: function() {
3525        return $(this.jqId + '_foot');
3526    },
3527
3528    updateData: function(data, clear) {
3529        var empty = (clear === undefined) ? true: clear;
3530
3531        if(empty)
3532            this.tbody.html(data);
3533        else
3534            this.tbody.append(data);
3535
3536        this.postUpdateData();
3537    },
3538
3539    postUpdateData: function() {
3540        if(this.cfg.draggableRows) {
3541            this.makeRowsDraggable();
3542        }
3543
3544        if(this.cfg.reflow) {
3545            this.initReflow();
3546        }
3547
3548        if(this.cfg.groupColumnIndexes) {
3549            this.groupRows();
3550            this.bindToggleRowGroupEvents();
3551        }
3552    },
3553
3554    //@override
3555    refresh: function(cfg) {
3556        this.columnWidthsFixed = false;
3557        this.ignoreRowHoverEvent = false;
3558
3559        this.unbindEvents();
3560
3561        this._super(cfg);
3562    },
3563
3564    /**
3565     * Unbinds events needed if refreshing to prevent multiple sort and pagination events.
3566     */
3567    unbindEvents: function() {
3568        if (this.sortableColumns) {
3569            this.sortableColumns.off();
3570        }
3571        if (this.paginator) {
3572            this.paginator.unbindEvents();
3573        }
3574    },
3575
3576    /**
3577     * Binds the change event listener and renders the paginator
3578     */
3579    bindPaginator: function() {
3580        var _self = this;
3581        this.cfg.paginator.paginate = function(newState) {
3582            if(_self.cfg.clientCache) {
3583                _self.loadDataWithCache(newState);
3584            }
3585            else {
3586                _self.paginate(newState);
3587            }
3588        };
3589
3590        this.paginator = new PrimeFaces.widget.Paginator(this.cfg.paginator);
3591        this.paginator.bindSwipeEvents(this.jq);
3592        
3593
3594        if(this.cfg.clientCache) {
3595            this.cacheRows = this.paginator.getRows();
3596            var newState = {
3597                first:  this.paginator.getFirst(),
3598                rows: this.paginator.getRows(),
3599                page: this.paginator.getCurrentPage()
3600            };
3601            this.clearCacheMap();
3602            this.fetchNextPage(newState);
3603        } 
3604    },
3605
3606    /**
3607     * Applies events related to sorting in a non-obstrusive way
3608     */
3609    bindSortEvents: function() {
3610        var $this = this,
3611            hasAriaSort = false;
3612        this.cfg.tabindex = this.cfg.tabindex||'0';
3613        this.headers = this.thead.find('> tr > th');
3614        this.sortableColumns = this.headers.filter('.ui-sortable-column');
3615        this.sortableColumns.attr('tabindex', this.cfg.tabindex);
3616
3617        //aria messages
3618        this.ascMessage = PrimeFaces.getAriaLabel('datatable.sort.ASC');
3619        this.descMessage = PrimeFaces.getAriaLabel('datatable.sort.DESC');
3620
3621        //reflow dropdown
3622        this.reflowDD = $(this.jqId + '_reflowDD');
3623
3624        if(this.cfg.multiSort) {
3625            this.sortMeta = [];
3626        }
3627
3628        for(var i = 0; i < this.sortableColumns.length; i++) {
3629            var columnHeader = this.sortableColumns.eq(i),
3630            columnHeaderId = columnHeader.attr('id'),
3631            sortIcon = columnHeader.children('span.ui-sortable-column-icon'),
3632            sortOrder = null,
3633            resolvedSortMetaIndex = null,
3634            ariaLabel = columnHeader.attr('aria-label');
3635
3636            if(columnHeader.hasClass('ui-state-active')) {
3637                if(sortIcon.hasClass('ui-icon-triangle-1-n')) {
3638                    sortOrder = this.SORT_ORDER.ASCENDING;
3639                    columnHeader.attr('aria-label', this.getSortMessage(ariaLabel, this.descMessage));
3640                    if(!hasAriaSort) {
3641                        columnHeader.attr('aria-sort', 'ascending');
3642                        hasAriaSort = true;
3643                    }
3644                }
3645                else {
3646                    sortOrder = this.SORT_ORDER.DESCENDING;
3647                    columnHeader.attr('aria-label', this.getSortMessage(ariaLabel, this.ascMessage));
3648                    if(!hasAriaSort) {
3649                        columnHeader.attr('aria-sort', 'descending');
3650                        hasAriaSort = true;
3651                    }
3652                }
3653
3654                if (this.cfg.multiSort && this.cfg.sortMetaOrder) {
3655                    resolvedSortMetaIndex = $.inArray(columnHeaderId, this.cfg.sortMetaOrder);
3656
3657                    this.sortMeta[resolvedSortMetaIndex] = {
3658                        col: columnHeaderId,
3659                        order: sortOrder
3660                    };
3661                }
3662
3663                $this.updateReflowDD(columnHeader, sortOrder);
3664            }
3665            else {
3666                sortOrder = this.SORT_ORDER.UNSORTED;
3667                columnHeader.attr('aria-label', this.getSortMessage(ariaLabel, this.ascMessage));
3668                if(!hasAriaSort && i == (this.sortableColumns.length - 1)) {
3669                    this.sortableColumns.eq(0).attr('aria-sort', 'other');
3670                    hasAriaSort = true;
3671                }
3672            }
3673
3674            columnHeader.data('sortorder', sortOrder);
3675        }
3676
3677        this.sortableColumns.on('mouseenter.dataTable', function() {
3678            var column = $(this);
3679
3680            if(!column.hasClass('ui-state-active'))
3681                column.addClass('ui-state-hover');
3682        })
3683        .on('mouseleave.dataTable', function() {
3684            var column = $(this);
3685
3686            if(!column.hasClass('ui-state-active'))
3687                column.removeClass('ui-state-hover');
3688        })
3689        .on('blur.dataTable', function() {
3690            $(this).removeClass('ui-state-focus');
3691        })
3692        .on('focus.dataTable', function() {
3693            $(this).addClass('ui-state-focus');
3694        })
3695        .on('keydown.dataTable', function(e) {
3696            var key = e.which,
3697            keyCode = $.ui.keyCode;
3698
3699            if((key === keyCode.ENTER) && $(e.target).is(':not(:input)')) {
3700                $(this).trigger('click.dataTable', (e.metaKey||e.ctrlKey));
3701                e.preventDefault();
3702            }
3703        })
3704        .on('click.dataTable', function(e, metaKeyOn) {
3705            if(!$this.shouldSort(e, this)) {
3706                return;
3707            }
3708
3709            PrimeFaces.clearSelection();
3710
3711            var columnHeader = $(this),
3712            sortOrderData = columnHeader.data('sortorder'),
3713            sortOrder = (sortOrderData === $this.SORT_ORDER.UNSORTED) ? $this.SORT_ORDER.ASCENDING : -1 * sortOrderData,
3714            metaKey = e.metaKey||e.ctrlKey||metaKeyOn;
3715
3716            if($this.cfg.multiSort) {
3717                if(metaKey) {
3718                    $this.addSortMeta({
3719                        col: columnHeader.attr('id'),
3720                        order: sortOrder
3721                    });
3722                    $this.sort(columnHeader, sortOrder, true);
3723                }
3724                else {
3725                    $this.sortMeta = [];
3726                    $this.addSortMeta({
3727                        col: columnHeader.attr('id'),
3728                        order: sortOrder
3729                    });
3730                    $this.sort(columnHeader, sortOrder);
3731                }
3732            }
3733            else {
3734                $this.sort(columnHeader, sortOrder);
3735            }
3736
3737            if($this.cfg.scrollable) {
3738                $(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).trigger('focus');
3739            }
3740
3741            $this.updateReflowDD(columnHeader, sortOrder);
3742        });
3743
3744        if(this.reflowDD && this.cfg.reflow) {
3745            PrimeFaces.skinSelect(this.reflowDD);
3746            this.reflowDD.change(function(e) {
3747                var arrVal = $(this).val().split('_'),
3748                    columnHeader = $this.sortableColumns.eq(parseInt(arrVal[0])),
3749                    sortOrder = parseInt(arrVal[1]);
3750
3751                    columnHeader.data('sortorder', sortOrder);
3752                    columnHeader.trigger('click.dataTable');
3753            });
3754        }
3755    },
3756
3757    getSortMessage: function(ariaLabel, sortOrderMessage) {
3758        var headerName = ariaLabel ? ariaLabel.split(':')[0] : '';
3759        return headerName + ': ' + sortOrderMessage;
3760    },
3761
3762    shouldSort: function(event, column) {
3763        if(this.isEmpty()) {
3764            return false;
3765        }
3766
3767        var target = $(event.target);
3768        if(target.closest('.ui-column-customfilter', column).length) {
3769            return false;
3770        }
3771
3772        return target.is('th,span');
3773    },
3774
3775    addSortMeta: function(meta) {
3776        this.sortMeta = $.grep(this.sortMeta, function(value) {
3777            return value.col !== meta.col;
3778        });
3779
3780        this.sortMeta.push(meta);
3781    },
3782
3783    /**
3784     * Binds filter events to standard filters
3785     */
3786    setupFiltering: function() {
3787        var $this = this,
3788        filterColumns = this.thead.find('> tr > th.ui-filter-column');
3789        this.cfg.filterEvent = this.cfg.filterEvent||'keyup';
3790        this.cfg.filterDelay = this.cfg.filterDelay||300;
3791
3792        filterColumns.children('.ui-column-filter').each(function() {
3793            var filter = $(this);
3794
3795            if(filter.is('input:text')) {
3796                PrimeFaces.skinInput(filter);
3797                $this.bindTextFilter(filter);
3798            }
3799            else {
3800                PrimeFaces.skinSelect(filter);
3801                $this.bindChangeFilter(filter);
3802            }
3803        });
3804    },
3805
3806    bindTextFilter: function(filter) {
3807        if(this.cfg.filterEvent === 'enter')
3808            this.bindEnterKeyFilter(filter);
3809        else
3810            this.bindFilterEvent(filter);
3811    },
3812
3813    bindChangeFilter: function(filter) {
3814        var $this = this;
3815
3816        filter.off('change')
3817        .on('change', function() {
3818            $this.filter();
3819        });
3820    },
3821
3822    bindEnterKeyFilter: function(filter) {
3823        var $this = this;
3824
3825        filter.off('keydown keyup')
3826        .on('keydown', PrimeFaces.utils.blockEnterKey)
3827        .on('keyup', function(e) {
3828            var key = e.which,
3829            keyCode = $.ui.keyCode;
3830
3831            if((key === keyCode.ENTER)) {
3832                $this.filter();
3833
3834                e.preventDefault();
3835            }
3836        });
3837    },
3838
3839    bindFilterEvent: function(filter) {
3840        var $this = this;
3841        var filterEventName = this.cfg.filterEvent + '.dataTable';
3842        
3843        //prevent form submit on enter key
3844        filter.off('keydown.dataTable-blockenter ' + filterEventName)
3845        .on('keydown.dataTable-blockenter', PrimeFaces.utils.blockEnterKey)
3846        .on(filterEventName, function(e) {
3847            if (PrimeFaces.utils.ignoreFilterKey(e)) {
3848                return;
3849            }
3850
3851            if($this.filterTimeout) {
3852                clearTimeout($this.filterTimeout);
3853            }
3854
3855            $this.filterTimeout = setTimeout(function() {
3856                $this.filter();
3857                $this.filterTimeout = null;
3858            },
3859            $this.cfg.filterDelay);
3860        });
3861
3862        // #89 IE clear "x" button
3863        if (PrimeFaces.env.isIE()) {
3864            filter.off('mouseup.dataTable').on('mouseup.dataTable', function(e) {
3865                var input = $(this),
3866                oldValue = input.val();
3867
3868                if(oldValue == "") {
3869                    return;
3870                }
3871
3872                setTimeout(function() {
3873                    var newValue = input.val();
3874                    if(newValue == "") {
3875                        $this.filter();
3876                    }
3877                }, 1);
3878            });
3879        }
3880    },
3881
3882    setupRowHover: function() {
3883        var selector = '> tr.ui-widget-content';
3884        if(!this.cfg.selectionMode || this.cfg.selectionMode === 'checkbox') {
3885            this.bindRowHover(selector);
3886        }
3887    },
3888
3889    setupSelection: function() {
3890        this.selectionHolder = this.jqId + '_selection';
3891        this.cfg.rowSelectMode = this.cfg.rowSelectMode||'new';
3892        this.rowSelector = '> tr.ui-widget-content.ui-datatable-selectable';
3893        this.cfg.disabledTextSelection = this.cfg.disabledTextSelection === false ? false : true;
3894        this.cfg.selectionPageOnly = this.cfg.selectionPageOnly !== false;
3895        this.rowSelectorForRowClick = this.cfg.rowSelector||'td:not(.ui-column-unselectable):not(.ui-grouped-column),span:not(.ui-c)';
3896
3897        var preselection = $(this.selectionHolder).val();
3898        this.selection = (preselection === "") ? [] : preselection.split(',');
3899
3900        //shift key based range selection
3901        this.originRowIndex = null;
3902        this.cursorIndex = null;
3903
3904        this.bindSelectionEvents();
3905    },
3906
3907    /**
3908     * Applies events related to selection in a non-obstrusive way
3909     */
3910    bindSelectionEvents: function() {
3911        if(this.cfg.selectionMode === 'radio') {
3912            this.bindRadioEvents();
3913        }
3914        else if(this.cfg.selectionMode === 'checkbox') {
3915            this.bindCheckboxEvents();
3916            this.updateHeaderCheckbox();
3917
3918            if(this.cfg.rowSelectMode !== 'checkbox') {
3919                this.bindRowEvents();
3920            }
3921        }
3922        else {
3923            this.bindRowEvents();
3924        }
3925    },
3926
3927    bindRowEvents: function() {
3928        var $this = this;
3929
3930        this.bindRowHover(this.rowSelector);
3931
3932        this.tbody.off('click.dataTable mousedown.dataTable', this.rowSelector).on('mousedown.dataTable', this.rowSelector, null, function(e) {
3933            $this.mousedownOnRow = true;
3934        })
3935        .on('click.dataTable', this.rowSelector, null, function(e) {
3936            $this.onRowClick(e, this);
3937            $this.mousedownOnRow = false;
3938        });
3939
3940        //double click
3941        if(this.hasBehavior('rowDblselect')) {
3942            this.tbody.off('dblclick.dataTable', this.rowSelector).on('dblclick.dataTable', this.rowSelector, null, function(e) {
3943                $this.onRowDblclick(e, $(this));
3944            });
3945        };
3946
3947        this.bindSelectionKeyEvents();
3948    },
3949
3950    bindSelectionKeyEvents: function() {
3951        var $this = this;
3952
3953        this.getFocusableTbody().on('focus', function(e) {
3954            //ignore mouse click on row
3955            if(!$this.mousedownOnRow) {
3956                $this.focusedRow = $this.tbody.children('tr.ui-widget-content.ui-datatable-selectable.ui-state-highlight').eq(0);
3957                if ($this.focusedRow.length == 0) {
3958                    $this.focusedRow = $this.tbody.children('tr.ui-widget-content.ui-datatable-selectable').eq(0);
3959                }
3960
3961                $this.highlightFocusedRow();
3962
3963                if($this.cfg.scrollable) {
3964                    PrimeFaces.scrollInView($this.scrollBody, $this.focusedRow);
3965                }
3966            }
3967        })
3968        .on('blur', function() {
3969            if($this.focusedRow) {
3970                $this.unhighlightFocusedRow();
3971                $this.focusedRow = null;
3972            }
3973        })
3974        .on('keydown', function(e) {
3975            var keyCode = $.ui.keyCode,
3976            key = e.which;
3977
3978            if($(e.target).is(':input')) {
3979                return;
3980            }
3981
3982            if($this.focusedRow) {
3983                switch(key) {
3984                    case keyCode.UP:
3985                    case keyCode.DOWN:
3986                        var rowSelector = 'tr.ui-widget-content.ui-datatable-selectable',
3987                        row = key === keyCode.UP ? $this.focusedRow.prev(rowSelector) : $this.focusedRow.next(rowSelector);
3988
3989                        if(row.length) {
3990                            $this.unhighlightFocusedRow();
3991
3992                            if($this.isCheckboxSelectionEnabled()) {
3993                                row.find('> td.ui-selection-column .ui-chkbox input').focus();
3994                            }
3995                            else {
3996                                $this.focusedRow = row;
3997                            }
3998
3999                            $this.highlightFocusedRow();
4000
4001                            if($this.cfg.scrollable) {
4002                                PrimeFaces.scrollInView($this.scrollBody, $this.focusedRow);
4003                            }
4004                        }
4005                        e.preventDefault();
4006                    break;
4007
4008                    case keyCode.ENTER:
4009                    case keyCode.SPACE:
4010                        if($this.focusedRowWithCheckbox) {
4011                            $this.focusedRow.find('> td.ui-selection-column .ui-chkbox .ui-chkbox-box').trigger('click.dataTable');
4012                        }
4013                        else {
4014                            e.target = $this.focusedRow.children().eq(0).get(0);
4015                            $this.onRowClick(e,$this.focusedRow.get(0));
4016                        }
4017
4018                        e.preventDefault();
4019                    break;
4020
4021                    default:
4022                    break;
4023                };
4024            }
4025        });
4026
4027    },
4028
4029    highlightFocusedRow: function() {
4030        this.focusedRow.addClass('ui-state-hover');
4031    },
4032
4033    unhighlightFocusedRow: function() {
4034        this.focusedRow.removeClass('ui-state-hover');
4035    },
4036
4037    assignFocusedRow: function(row) {
4038        this.focusedRow = row;
4039    },
4040    
4041    bindRowHover: function(rowSelector) {
4042        var $this = this;
4043        this.tbody.off('mouseenter.dataTable mouseleave.dataTable', rowSelector)
4044            .on('mouseenter.dataTable', rowSelector, null, function() {
4045                var element = $(this);
4046                if (!$this.ignoreRowHoverEvent && !element.hasClass('ui-state-highlight')) {
4047                    $(this).addClass('ui-state-hover');
4048                }
4049            })
4050            .on('mouseleave.dataTable', rowSelector, null, function() {
4051                var element = $(this);
4052                if (!$this.ignoreRowHoverEvent && !element.hasClass('ui-state-highlight')) {
4053                    $(this).removeClass('ui-state-hover');
4054                }
4055            });
4056
4057        if (this.cfg.groupColumnIndexes) {
4058            var columnSelector = rowSelector + ' > td';
4059            this.tbody.off('mouseenter.dataTable mouseleave.dataTable', columnSelector)
4060                .on('mouseenter.dataTable', columnSelector, null, function() {
4061                    var row = $(this).parent();
4062                    if ($(this).hasClass('ui-grouped-column')) {
4063                        row.removeClass('ui-state-hover');
4064                        $this.ignoreRowHoverEvent = true;
4065                    }
4066                    else if (!row.hasClass('ui-state-highlight')) {
4067                        row.addClass('ui-state-hover');
4068                    }
4069                })
4070                .on('mouseleave.dataTable', columnSelector, null, function() {
4071                    if (!$(this).hasClass('ui-grouped-column')) {
4072                        $this.ignoreRowHoverEvent = false;
4073                    }
4074                });
4075        }
4076    },
4077
4078    bindRadioEvents: function() {
4079        var $this = this,
4080        radioInputSelector = '> tr.ui-widget-content:not(.ui-datatable-empty-message) > td.ui-selection-column :radio';
4081
4082        if(this.cfg.nativeElements) {
4083            this.tbody.off('click.dataTable', radioInputSelector).on('click.dataTable', radioInputSelector, null, function(e) {
4084                var radioButton = $(this);
4085
4086                if(!radioButton.prop('checked'))
4087                    $this.selectRowWithRadio(radioButton);
4088            });
4089        }
4090        else {
4091            var radioSelector = '> tr.ui-widget-content:not(.ui-datatable-empty-message) > td.ui-selection-column .ui-radiobutton .ui-radiobutton-box';
4092            this.tbody.off('click.dataTable mouseover.dataTable mouseout.dataTable', radioSelector)
4093                .on('mouseover.dataTable', radioSelector, null, function() {
4094                    var radio = $(this);
4095                    if(!radio.hasClass('ui-state-disabled')&&!radio.hasClass('ui-state-active')) {
4096                        radio.addClass('ui-state-hover');
4097                    }
4098                })
4099                .on('mouseout.dataTable', radioSelector, null, function() {
4100                    var radio = $(this);
4101                    radio.removeClass('ui-state-hover');
4102                })
4103                .on('click.dataTable', radioSelector, null, function() {
4104                    var radio = $(this),
4105                    checked = radio.hasClass('ui-state-active'),
4106                    disabled = radio.hasClass('ui-state-disabled');
4107
4108                    if(!disabled && !checked) {
4109                        $this.selectRowWithRadio(radio);
4110                    }
4111                });
4112        }
4113
4114        //keyboard support
4115        this.tbody.off('focus.dataTable blur.dataTable change.dataTable', radioInputSelector)
4116            .on('focus.dataTable', radioInputSelector, null, function() {
4117                var input = $(this),
4118                box = input.parent().next();
4119
4120                if(input.prop('checked')) {
4121                    box.removeClass('ui-state-active');
4122                }
4123
4124                box.addClass('ui-state-focus');
4125            })
4126            .on('blur.dataTable', radioInputSelector, null, function() {
4127                var input = $(this),
4128                box = input.parent().next();
4129
4130                if(input.prop('checked')) {
4131                    box.addClass('ui-state-active');
4132                }
4133
4134                box.removeClass('ui-state-focus');
4135            })
4136            .on('change.dataTable', radioInputSelector, null, function() {
4137                var currentInput = $this.tbody.find(radioInputSelector).filter(':checked'),
4138                currentRadio = currentInput.parent().next();
4139
4140                $this.selectRowWithRadio(currentRadio);
4141            });
4142
4143    },
4144
4145    bindCheckboxEvents: function() {
4146        var $this = this,
4147        checkboxInputSelector = '> tr.ui-widget-content.ui-datatable-selectable > td.ui-selection-column :checkbox';
4148
4149        if(this.cfg.nativeElements) {
4150            this.checkAllToggler = this.thead.find('> tr > th.ui-selection-column > :checkbox');
4151            this.checkAllTogglerInput = this.checkAllToggler;
4152
4153            this.checkAllToggler.on('click', function() {
4154                $this.toggleCheckAll();
4155            });
4156
4157            this.tbody.off('click.dataTable', checkboxInputSelector).on('click.dataTable', checkboxInputSelector, null, function(e) {
4158                var checkbox = $(this);
4159
4160                if(checkbox.prop('checked'))
4161                    $this.selectRowWithCheckbox(checkbox);
4162                else
4163                    $this.unselectRowWithCheckbox(checkbox);
4164            });
4165        }
4166        else {
4167            this.checkAllToggler = this.thead.find('> tr > th.ui-selection-column > .ui-chkbox.ui-chkbox-all > .ui-chkbox-box');
4168            this.checkAllTogglerInput = this.checkAllToggler.prev().children(':checkbox');
4169
4170            this.checkAllToggler.on('mouseover', function() {
4171                var box = $(this);
4172                if(!box.hasClass('ui-state-disabled')&&!box.hasClass('ui-state-active')) {
4173                    box.addClass('ui-state-hover');
4174                }
4175            })
4176            .on('mouseout', function() {
4177                $(this).removeClass('ui-state-hover');
4178            })
4179            .on('click', function() {
4180                var box = $(this);
4181                if(!box.hasClass('ui-state-disabled')) {
4182                    $this.toggleCheckAll();
4183                }
4184            });
4185
4186            var checkboxSelector = '> tr.ui-widget-content.ui-datatable-selectable > td.ui-selection-column .ui-chkbox .ui-chkbox-box';
4187            this.tbody.off('mouseover.dataTable mouseover.dataTable click.dataTable', checkboxSelector)
4188                        .on('mouseover.dataTable', checkboxSelector, null, function() {
4189                            var box = $(this);
4190                            if(!box.hasClass('ui-state-active')) {
4191                                box.addClass('ui-state-hover');
4192                            }
4193                        })
4194                        .on('mouseout.dataTable', checkboxSelector, null, function() {
4195                            $(this).removeClass('ui-state-hover');
4196                        })
4197                        .on('click.dataTable', checkboxSelector, null, function() {
4198                            var checkbox = $(this),
4199                            input = checkbox.prev().children('input');
4200
4201                            if(input.prop('checked'))
4202                                $this.unselectRowWithCheckbox(checkbox);
4203                            else
4204                                $this.selectRowWithCheckbox(checkbox);
4205                        });
4206        }
4207
4208        //keyboard support
4209        this.tbody.off('focus.dataTable blur.dataTable change.dataTable', checkboxInputSelector)
4210                    .on('focus.dataTable', checkboxInputSelector, null, function() {
4211                        var input = $(this),
4212                        box = input.parent().next();
4213
4214                        if(input.prop('checked')) {
4215                            box.removeClass('ui-state-active');
4216                        }
4217
4218                        box.addClass('ui-state-focus');
4219
4220                        $this.focusedRow = input.closest('.ui-datatable-selectable');
4221                        $this.focusedRowWithCheckbox = true;
4222                    })
4223                    .on('blur.dataTable', checkboxInputSelector, null, function() {
4224                        var input = $(this),
4225                        box = input.parent().next();
4226
4227                        if(input.prop('checked')) {
4228                            box.addClass('ui-state-active');
4229                        }
4230
4231                        box.removeClass('ui-state-focus');
4232
4233                        $this.unhighlightFocusedRow();
4234                        $this.focusedRow = null;
4235                        $this.focusedRowWithCheckbox = false;
4236                    })
4237                    .on('change.dataTable', checkboxInputSelector, null, function(e) {
4238                        var input = $(this),
4239                        box = input.parent().next();
4240
4241                        if(input.prop('checked')) {
4242                            $this.selectRowWithCheckbox(box);
4243                        }
4244                        else {
4245                            $this.unselectRowWithCheckbox(box);
4246                        }
4247                    });
4248
4249        this.checkAllTogglerInput.on('focus.dataTable', function(e) {
4250                        var input = $(this),
4251                        box = input.parent().next();
4252
4253                        if(!box.hasClass('ui-state-disabled')) {
4254                            if(input.prop('checked')) {
4255                                box.removeClass('ui-state-active');
4256                            }
4257
4258                            box.addClass('ui-state-focus');
4259                        }
4260                    })
4261                    .on('blur.dataTable', function(e) {
4262                        var input = $(this),
4263                        box = input.parent().next();
4264
4265                        if(input.prop('checked')) {
4266                            box.addClass('ui-state-active');
4267                        }
4268
4269                        box.removeClass('ui-state-focus');
4270                    })
4271                    .on('change.dataTable', function(e) {
4272                        var input = $(this),
4273                        box = input.parent().next();
4274
4275                        if(!box.hasClass('ui-state-disabled')) {
4276                            if(!input.prop('checked')) {
4277                                box.addClass('ui-state-active');
4278                            }
4279
4280                            $this.toggleCheckAll();
4281
4282                            if(input.prop('checked')) {
4283                                box.removeClass('ui-state-active').addClass('ui-state-focus');
4284                            }
4285                        }
4286                    });
4287    },
4288
4289    toggleRow: function(row) {
4290        if(row && !this.isRowTogglerClicked) {
4291            var toggler = row.find('> td > div.ui-row-toggler');
4292            this.toggleExpansion(toggler);
4293        }
4294        this.isRowTogglerClicked = false;
4295    },
4296
4297    /**
4298     * Applies events related to row expansion in a non-obstrusive way
4299     */
4300    bindExpansionEvents: function() {
4301        var $this = this,
4302        togglerSelector = '> tr > td > div.ui-row-toggler';
4303
4304        this.tbody.off('click.datatable-expansion', togglerSelector)
4305            .on('click.datatable-expansion', togglerSelector, null, function() {
4306                $this.isRowTogglerClicked = true;
4307                $this.toggleExpansion($(this));
4308            })
4309            .on('keydown.datatable-expansion', togglerSelector, null, function(e) {
4310                var key = e.which,
4311                keyCode = $.ui.keyCode;
4312
4313                if((key === keyCode.ENTER)) {
4314                    $this.toggleExpansion($(this));
4315                    e.preventDefault();
4316                }
4317            });
4318    },
4319
4320    bindContextMenu : function(menuWidget, targetWidget, targetId, cfg) {
4321        var $this = this;
4322        var targetSelector = targetId + ' tbody.ui-datatable-data > tr.ui-widget-content';
4323        var targetEvent = cfg.event + '.datatable';
4324        this.contextMenuWidget = menuWidget;
4325
4326        $(document).off(targetEvent, targetSelector).on(targetEvent, targetSelector, null, function(e) {
4327            var row = $(this);
4328
4329            if(targetWidget.cfg.selectionMode && row.hasClass('ui-datatable-selectable')) {
4330                targetWidget.onRowRightClick(e, this, cfg.selectionMode);
4331
4332                menuWidget.show(e);
4333            }
4334            else if(targetWidget.cfg.editMode === 'cell') {
4335                var target = $(e.target),
4336                cell = target.is('td.ui-editable-column') ? target : target.parents('td.ui-editable-column:first');
4337
4338                if(targetWidget.contextMenuCell) {
4339                    targetWidget.contextMenuCell.removeClass('ui-state-highlight');
4340                }
4341
4342                targetWidget.contextMenuClick = true;
4343                targetWidget.contextMenuCell = cell;
4344                targetWidget.contextMenuCell.addClass('ui-state-highlight');
4345
4346                menuWidget.show(e);
4347            }
4348            else if(row.hasClass('ui-datatable-empty-message') && !$this.cfg.disableContextMenuIfEmpty) {
4349                menuWidget.show(e);
4350            }
4351        });
4352
4353        if(this.cfg.scrollable && this.scrollBody) {
4354            this.scrollBody.off('scroll.dataTable-contextmenu').on('scroll.dataTable-contextmenu', function() {
4355                if($this.contextMenuWidget.jq.is(':visible')) {
4356                    $this.contextMenuWidget.hide();
4357                }
4358            });
4359        }
4360    },
4361
4362    bindRowClick: function() {
4363        var $this = this,
4364        rowSelector = '> tr.ui-widget-content:not(.ui-expanded-row-content)';
4365        this.tbody.off('click.dataTable-rowclick', rowSelector).on('click.dataTable-rowclick', rowSelector, null, function(e) {
4366            var target = $(e.target),
4367            row = target.is('tr.ui-widget-content') ? target : target.closest('tr.ui-widget-content');
4368
4369            $this.cfg.onRowClick.call(this, row);
4370        });
4371    },
4372
4373    initReflow: function() {
4374        var headerColumns = this.thead.find('> tr > th');
4375
4376        for(var i = 0; i < headerColumns.length; i++) {
4377            var headerColumn = headerColumns.eq(i),
4378            reflowHeaderText = headerColumn.find('.ui-reflow-headertext:first').text(),
4379            colTitleEl = headerColumn.children('.ui-column-title'),
4380            title = (reflowHeaderText && reflowHeaderText.length) ? reflowHeaderText : colTitleEl.text();
4381            this.tbody.find('> tr:not(.ui-datatable-empty-message) > td:nth-child(' + (i + 1) + ')').prepend('<span class="ui-column-title">' + PrimeFaces.escapeHTML(title) + '</span>');
4382        }
4383    },
4384
4385    setupScrolling: function() {
4386        this.scrollHeader = this.jq.children('.ui-datatable-scrollable-header');
4387        this.scrollBody = this.jq.children('.ui-datatable-scrollable-body');
4388        this.scrollFooter = this.jq.children('.ui-datatable-scrollable-footer');
4389        this.scrollStateHolder = $(this.jqId + '_scrollState');
4390        this.scrollHeaderBox = this.scrollHeader.children('div.ui-datatable-scrollable-header-box');
4391        this.scrollFooterBox = this.scrollFooter.children('div.ui-datatable-scrollable-footer-box');
4392        this.headerTable = this.scrollHeaderBox.children('table');
4393        this.bodyTable = this.cfg.virtualScroll ? this.scrollBody.children('div').children('table') : this.scrollBody.children('table');
4394        this.footerTable = this.scrollFooter.children('table');
4395        this.footerCols = this.scrollFooter.find('> .ui-datatable-scrollable-footer-box > table > tfoot > tr > td');
4396        this.percentageScrollHeight = this.cfg.scrollHeight && (this.cfg.scrollHeight.indexOf('%') !== -1);
4397        this.percentageScrollWidth = this.cfg.scrollWidth && (this.cfg.scrollWidth.indexOf('%') !== -1);
4398        var $this = this,
4399        scrollBarWidth = this.getScrollbarWidth() + 'px',
4400        hScrollWidth = this.scrollBody[0].scrollWidth;
4401
4402        if(this.cfg.scrollHeight) {
4403            if(this.percentageScrollHeight) {
4404                this.adjustScrollHeight();
4405            }
4406
4407            if(this.hasVerticalOverflow()) {
4408                this.scrollHeaderBox.css('margin-right', scrollBarWidth);
4409                this.scrollFooterBox.css('margin-right', scrollBarWidth);
4410            }
4411        }
4412
4413        this.fixColumnWidths();
4414
4415        if(this.cfg.scrollWidth) {
4416            if(this.percentageScrollWidth)
4417                this.adjustScrollWidth();
4418            else
4419                this.setScrollWidth(parseInt(this.cfg.scrollWidth));
4420        }
4421
4422        this.cloneHead();
4423
4424        this.restoreScrollState();
4425
4426        if(this.cfg.liveScroll) {
4427            this.scrollOffset = 0;
4428            this.cfg.liveScrollBuffer = (100 - this.cfg.liveScrollBuffer) / 100;
4429            this.shouldLiveScroll = true;
4430            this.loadingLiveScroll = false;
4431            this.allLoadedLiveScroll = $this.cfg.scrollStep >= $this.cfg.scrollLimit;
4432        }
4433
4434        if(this.cfg.virtualScroll) {
4435            var row = this.bodyTable.children('tbody').children('tr.ui-widget-content');
4436            if(row) {
4437                var hasEmptyMessage = row.eq(0).hasClass('ui-datatable-empty-message'),
4438                scrollLimit = $this.cfg.scrollLimit;
4439
4440                if(hasEmptyMessage) {
4441                    scrollLimit = 1;
4442                    $this.bodyTable.css('top', '0px');
4443                }
4444
4445                this.rowHeight = row.outerHeight();
4446                this.scrollBody.children('div').css('height', parseFloat((scrollLimit * this.rowHeight + 1) + 'px'));
4447
4448                if(hasEmptyMessage && this.cfg.scrollHeight && this.percentageScrollHeight) {
4449                    setTimeout(function() {
4450                        $this.adjustScrollHeight();
4451                    }, 10);
4452                }
4453            }
4454        }
4455
4456        this.scrollBody.on('scroll.dataTable', function() {
4457            var scrollLeft = $this.scrollBody.scrollLeft();
4458            
4459            if ($this.isRTL) {
4460                $this.scrollHeaderBox.css('margin-right', (scrollLeft - hScrollWidth + this.clientWidth));
4461                $this.scrollFooterBox.css('margin-right', (scrollLeft - hScrollWidth + this.clientWidth));
4462            }
4463            else {
4464                $this.scrollHeaderBox.css('margin-left', -scrollLeft);
4465                $this.scrollFooterBox.css('margin-left', -scrollLeft);
4466            }
4467
4468            if($this.isEmpty()) {
4469                return;
4470            }
4471
4472            if($this.cfg.virtualScroll) {
4473                var virtualScrollBody = this;
4474
4475                clearTimeout($this.scrollTimeout);
4476                $this.scrollTimeout = setTimeout(function() {
4477                    var viewportHeight = $this.scrollBody.outerHeight(),
4478                    tableHeight = $this.bodyTable.outerHeight(),
4479                    pageHeight = $this.rowHeight * $this.cfg.scrollStep,
4480                    virtualTableHeight = parseFloat(($this.cfg.scrollLimit * $this.rowHeight) + 'px'),
4481                    pageCount = (virtualTableHeight / pageHeight)||1;
4482
4483                    if(virtualScrollBody.scrollTop + viewportHeight > parseFloat($this.bodyTable.css('top')) + tableHeight || virtualScrollBody.scrollTop < parseFloat($this.bodyTable.css('top'))) {
4484                        var page = Math.floor((virtualScrollBody.scrollTop * pageCount) / (virtualScrollBody.scrollHeight)) + 1;
4485                        $this.loadRowsWithVirtualScroll(page, function () {
4486                            $this.bodyTable.css('top', ((page - 1) * pageHeight) + 'px');
4487                        });
4488                    }
4489                }, 200);
4490            }
4491            else if($this.shouldLiveScroll) {
4492                var scrollTop = Math.ceil(this.scrollTop),
4493                scrollHeight = this.scrollHeight,
4494                viewportHeight = this.clientHeight;
4495
4496                if((scrollTop >
4496= ((scrollHeight * $this.cfg.liveScrollBuffer) - (viewportHeight))) && $this.shouldLoadLiveScroll()) {
4497                    $this.loadLiveRows();
4498                }
4499            }
4500
4501            $this.saveScrollState();
4502        });
4503
4504        this.scrollHeader.on('scroll.dataTable', function() {
4505            $this.scrollHeader.scrollLeft(0);
4506        });
4507
4508        this.scrollFooter.on('scroll.dataTable', function() {
4509            $this.scrollFooter.scrollLeft(0);
4510        });
4511
4512        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.jq, function() {
4513            if ($this.percentageScrollHeight) {
4514                $this.adjustScrollHeight();
4515            }
4516            if ($this.percentageScrollWidth) {
4517                $this.adjustScrollWidth();
4518            }
4519        });
4520    },
4521
4522    shouldLoadLiveScroll: function() {
4523        return (!this.loadingLiveScroll && !this.allLoadedLiveScroll);
4524    },
4525
4526    /**
4527     * Clones a table header and removes duplicate ids.
4528     */
4529    cloneTableHeader: function(thead, table) {
4530        var clone = thead.clone();
4531        clone.find('th').each(function() {
4532            var header = $(this);
4533            header.attr('id', header.attr('id') + '_clone');
4534            $(this).children().not('.ui-column-title').remove();
4535            $(this).children('.ui-column-title').children().remove();
4536        });
4537        clone.removeAttr('id').addClass('ui-datatable-scrollable-theadclone').height(0).prependTo(table);
4538
4539        return clone;
4540    },
4541
4542    cloneHead: function() {
4543        var $this = this;
4544
4545        if (this.theadClone) {
4546            this.theadClone.remove();
4547        }
4548        this.theadClone = this.cloneTableHeader(this.thead, this.bodyTable);
4549
4550        //reflect events from clone to original
4551        if(this.sortableColumns.length) {
4552            this.sortableColumns.removeAttr('tabindex').off('blur.dataTable focus.dataTable keydown.dataTable');
4553
4554            var clonedColumns = this.theadClone.find('> tr > th'),
4555            clonedSortableColumns = clonedColumns.filter('.ui-sortable-column');
4556            clonedColumns.each(function() {
4557                var col = $(this),
4558                originalId = col.attr('id').split('_clone')[0];
4559                if(col.hasClass('ui-sortable-column')) {
4560                    col.data('original', originalId);
4561                }
4562
4563                $this.setOuterWidth($(PrimeFaces.escapeClientId(originalId)), col[0].style.width);
4564            });
4565
4566            clonedSortableColumns.on('blur.dataTable', function() {
4567                $(PrimeFaces.escapeClientId($(this).data('original'))).removeClass('ui-state-focus');
4568            })
4569            .on('focus.dataTable', function() {
4570                $(PrimeFaces.escapeClientId($(this).data('original'))).addClass('ui-state-focus');
4571            })
4572            .on('keydown.dataTable', function(e) {
4573                var key = e.which,
4574                keyCode = $.ui.keyCode;
4575
4576                if((key === keyCode.ENTER) && $(e.target).is(':not(:input)')) {
4577                    $(PrimeFaces.escapeClientId($(this).data('original'))).trigger('click.dataTable', (e.metaKey||e.ctrlKey));
4578                    e.preventDefault();
4579                }
4580            });
4581        }
4582    },
4583
4584    adjustScrollHeight: function() {
4585        var relativeHeight = this.jq.parent().innerHeight() * (parseInt(this.cfg.scrollHeight) / 100),
4586        headerChilden = this.jq.children('.ui-datatable-header'),
4587        footerChilden = this.jq.children('.ui-datatable-footer'),
4588        tableHeaderHeight = (headerChilden.length > 0) ? headerChilden.outerHeight(true) : 0,
4589        tableFooterHeight = (footerChilden.length > 0) ? footerChilden.outerHeight(true) : 0,
4590        scrollersHeight = (this.scrollHeader.outerHeight(true) + this.scrollFooter.outerHeight(true)),
4591        paginatorsHeight = this.paginator ? this.paginator.getContainerHeight(true) : 0,
4592        height = (relativeHeight - (scrollersHeight + paginatorsHeight + tableHeaderHeight + tableFooterHeight));
4593
4594        if(this.cfg.virtualScroll) {
4595            this.scrollBody.css('max-height', height);
4596        }
4597        else {
4598            this.scrollBody.height(height);
4599        }
4600    },
4601
4602    adjustScrollWidth: function() {
4603        var width = parseInt((this.jq.parent().innerWidth() * (parseInt(this.cfg.scrollWidth) / 100)));
4604        this.setScrollWidth(width);
4605    },
4606
4607    setOuterWidth: function(element, width) {
4608        if (element.css('box-sizing') === 'border-box') { // Github issue: #5014
4609            var diff = element.outerWidth() - element.width();
4610            element.width(parseFloat(width) - diff);
4611        }
4612        else {
4613            element.width(width);
4614        }
4615    },
4616
4617    setScrollWidth: function(width) {
4618        var $this = this;
4619        this.jq.children('.ui-widget-header').each(function() {
4620            $this.setOuterWidth($(this), width);
4621        });
4622        this.scrollHeader.width(width);
4623        this.scrollBody.css('margin-right', 0).width(width);
4624        this.scrollFooter.width(width);
4625    },
4626
4627    alignScrollBody: function() {
4628        var marginRight = this.hasVerticalOverflow() ? this.getScrollbarWidth() + 'px' : '0px';
4629
4630        this.scrollHeaderBox.css('margin-right', marginRight);
4631        this.scrollFooterBox.css('margin-right', marginRight);
4632    },
4633
4634    getScrollbarWidth: function() {
4635        if(!this.scrollbarWidth) {
4636            this.scrollbarWidth = PrimeFaces.env.browser.webkit ? '15' : PrimeFaces.calculateScrollbarWidth();
4637        }
4638
4639        return this.scrollbarWidth;
4640    },
4641
4642    hasVerticalOverflow: function() {
4643        return (this.cfg.scrollHeight && this.bodyTable.outerHeight() > this.scrollBody.outerHeight());
4644    },
4645
4646    restoreScrollState: function() {
4647        var scrollState = this.scrollStateHolder.val(),
4648        scrollValues = scrollState.split(',');
4649
4650        if (scrollValues[0] == '-1') {
4651            scrollValues[0] = this.scrollBody[0].scrollWidth;
4652        }
4653        
4654        this.scrollBody.scrollLeft(scrollValues[0]);
4655        this.scrollBody.scrollTop(scrollValues[1]);
4656    },
4657
4658    saveScrollState: function() {
4659        var scrollState = this.scrollBody.scrollLeft() + ',' + this.scrollBody.scrollTop();
4660
4661        this.scrollStateHolder.val(scrollState);
4662    },
4663
4664    clearScrollState: function() {
4665        this.scrollStateHolder.val('0,0');
4666    },
4667
4668    fixColumnWidths: function() {
4669        var $this = this;
4670
4671        if(!this.columnWidthsFixed) {
4672            if(this.cfg.scrollable) {
4673                this.scrollHeader.find('> .ui-datatable-scrollable-header-box > table > thead > tr > th').each(function() {
4674                    var headerCol = $(this),
4675                    colIndex = headerCol.index(),
4676                    headerStyle = headerCol[0].style,
4677                    width = headerStyle.width||headerCol.width();
4678
4679                    if($this.cfg.multiViewState && $this.resizableStateHolder && $this.resizableStateHolder.attr('value')) {
4680                        width = ($this.findColWidthInResizableState(headerCol.attr('id')) || width);
4681                    }
4682
4683                    $this.setOuterWidth(headerCol, width);
4684
4685                    if($this.footerCols.length > 0) {
4686                        var footerCol = $this.footerCols.eq(colIndex);
4687                        $this.setOuterWidth(footerCol, width);
4688                    }
4689                });
4690            }
4691            else {
4692                var columns = this.jq.find('> .ui-datatable-tablewrapper > table > thead > tr > th'),
4693                    visibleColumns = columns.filter(':visible'),
4694                    hiddenColumns = columns.filter(':hidden');
4695
4696                this.setColumnsWidth(visibleColumns);
4697                /* IE fixes */
4698                this.setColumnsWidth(hiddenColumns);
4699            }
4700
4701            this.columnWidthsFixed = true;
4702        }
4703    },
4704
4705    setColumnsWidth: function(columns) {
4706        if(columns.length) {
4707            var $this = this;
4708
4709            columns.each(function() {
4710                var col = $(this),
4711                colStyle = col[0].style,
4712                width = colStyle.width||col.width();
4713
4714                if($this.cfg.multiViewState && $this.resizableStateHolder && $this.resizableStateHolder.attr('value')) {
4715                    width = ($this.findColWidthInResizableState(col.attr('id')) || width);
4716                }
4717
4718                col.width(width);
4719            });
4720        }
4721    },
4722
4723    /**
4724     * Loads rows on-the-fly when scrolling live
4725     */
4726    loadLiveRows: function() {
4727        if(this.liveScrollActive||(this.scrollOffset + this.cfg.scrollStep > this.cfg.scrollLimit)) {
4728            return;
4729        }
4730
4731        this.liveScrollActive = true;
4732        this.scrollOffset += this.cfg.scrollStep;
4733
4734        //Disable scroll if there is no more data left
4735        if(this.scrollOffset === this.cfg.scrollLimit) {
4736            this.shouldLiveScroll = false;
4737        }
4738
4739        var $this = this,
4740        options = {
4741            source: this.id,
4742            process: this.id,
4743            update: this.id,
4744            formId: this.cfg.formId,
4745            params: [{name: this.id + '_scrolling', value: true},
4746                            {name: this.id + '_skipChildren', value: true},
4747                            {name: this.id + '_scrollOffset', value: this.scrollOffset},
4748                            {name: this.id + '_encodeFeature', value: true}],
4749            onsuccess: function(responseXML, status, xhr) {
4750                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4751                    widget: $this,
4752                    handle: function(content) {
4753                        //insert new rows
4754                        this.updateData(content, false);
4755
4756                        this.liveScrollActive = false;
4757                    }
4758                });
4759
4760                return true;
4761            },
4762            oncomplete: function(xhr, status, args, data) {
4763                if(typeof args.totalRecords !== 'undefined') {
4764                    $this.cfg.scrollLimit = args.totalRecords;
4765                }
4766
4767                $this.loadingLiveScroll = false;
4768                $this.allLoadedLiveScroll = ($this.scrollOffset + $this.cfg.scrollStep) >= $this.cfg.scrollLimit;
4769
4770                // reset index of shift selection on multiple mode
4771                $this.originRowIndex = null;
4772            }
4773        };
4774
4775        PrimeFaces.ajax.Request.handle(options);
4776    },
4777
4778    loadRowsWithVirtualScroll: function(page, callback) {
4779        if(this.virtualScrollActive) {
4780            return;
4781        }
4782
4783        this.virtualScrollActive = true;
4784
4785        var $this = this,
4786        first = (page - 1) * this.cfg.scrollStep,
4787        options = {
4788            source: this.id,
4789            process: this.id,
4790            update: this.id,
4791            formId: this.cfg.formId,
4792            params: [{name: this.id + '_scrolling', value: true},
4793                            {name: this.id + '_skipChildren', value: true},
4794                            {name: this.id + '_first', value: first},
4795                            {name: this.id + '_encodeFeature', value: true}],
4796            onsuccess: function(responseXML, status, xhr) {
4797                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4798                    widget: $this,
4799                    handle: function(content) {
4800                        //insert new rows
4801                        this.updateData(content);
4802                        callback();
4803                        this.virtualScrollActive = false;
4804                    }
4805                });
4806
4807                return true;
4808            },
4809            oncomplete: function(xhr, status, args, data) {
4810                if(typeof args.totalRecords !== 'undefined') {
4811                    $this.cfg.scrollLimit = args.totalRecords;
4812                }
4813
4814                // reset index of shift selection on multiple mode
4815                $this.originRowIndex = null;
4816            }
4817        };
4818        if (this.hasBehavior('virtualScroll')) {
4819            this.callBehavior('virtualScroll', options);
4820        } else {
4821            PrimeFaces.ajax.Request.handle(options);
4822        }
4823    },
4824
4825    /**
4826     * Ajax pagination
4827     */
4828    paginate: function(newState) {
4829        var $this = this,
4830        options = {
4831            source: this.id,
4832            update: this.id,
4833            process: this.id,
4834            formId: this.cfg.formId,
4835            params: [{name: this.id + '_pagination', value: true},
4836                    {name: this.id + '_first', value: newState.first},
4837                    {name: this.id + '_rows', value: newState.rows},
4838                    {name: this.id + '_skipChildren', value: true},
4839                    {name: this.id + '_encodeFeature', value: true}]
4840        };
4841        
4842        if (!this.cfg.partialUpdate) {
4843            options.params.push({name: this.id + '_fullUpdate', value: true});
4844
4845            options.onsuccess = function(responseXML, status, xhr) {
4846                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4847                        widget: $this,
4848                        handle: function(content) {
4849                            this.jq.replaceWith(content);
4850                        }
4851                    });
4852
4853                return true;
4854            };
4855        }
4856        else {
4857            options.onsuccess = function(responseXML, status, xhr) {
4858                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4859                        widget: $this,
4860                        handle: function(content) {
4861                            this.updateData(content);
4862
4863                            if(this.checkAllToggler) {
4864                                this.updateHeaderCheckbox();
4865                            }
4866
4867                            if(this.cfg.scrollable) {
4868                                this.alignScrollBody();
4869                            }
4870
4871                            if(this.cfg.clientCache) {
4872                                this.cacheMap[newState.first] = content;
4873                            }
4874                        }
4875                    });
4876
4877                return true;
4878            };
4879
4880            options.oncomplete = function(xhr, status, args, data) {
4881                $this.paginator.cfg.page = newState.page;
4882                if(args && typeof args.totalRecords !== 'undefined') {
4883                    $this.paginator.updateTotalRecords(args.totalRecords);
4884                }
4885                else {
4886                    $this.paginator.updateUI();
4887                }
4888                $this.updateColumnsView();
4889                // reset index of shift selection on multiple mode
4890                $this.originRowIndex = null;
4891            };
4892        }
4893
4894        if(this.hasBehavior('page')) {
4895            this.callBehavior('page', options);
4896        }
4897        else {
4898            PrimeFaces.ajax.Request.handle(options);
4899        }
4900    },
4901
4902    /**
4903     * Loads next page asynchronously to keep it at viewstate and Upd
4903ates viewstate
4904     */
4905    fetchNextPage: function(newState) {
4906        var rows = newState.rows,
4907        first = newState.first,
4908        $this = this,
4909        options = {
4910            source: this.id,
4911            process: this.id,
4912            update: this.id,
4913            global: false,
4914            params: [{name: this.id + '_skipChildren', value: true},
4915                    {name: this.id + '_encodeFeature', value: true},
4916                    {name: this.id + '_first', value: first},
4917                    {name: this.id + '_rows', value: rows},
4918                    {name: this.id + '_pagination', value: true},
4919                    {name: this.id + '_clientCache', value: true}],
4920            onsuccess: function(responseXML, status, xhr) {
4921                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4922                    widget: $this,
4923                    handle: function(content) {
4924                        if(content.length) {
4925                            var nextFirstValue = first + rows;
4926                            $this.cacheMap[nextFirstValue] = content;
4927                        }
4928                    }
4929                });
4930
4931                return true;
4932            }
4933        };
4934
4935        PrimeFaces.ajax.Request.handle(options);
4936    },
4937
4938    updatePageState: function(newState) {
4939        var $this = this,
4940        options = {
4941            source: this.id,
4942            process: this.id,
4943            update: this.id,
4944            global: false,
4945            params: [{name: this.id + '_pagination', value: true},
4946                    {name: this.id + '_encodeFeature', value: true},
4947                    {name: this.id + '_pageState', value: true},
4948                    {name: this.id + '_first', value: newState.first},
4949                    {name: this.id + '_rows', value: newState.rows}],
4950            onsuccess: function(responseXML, status, xhr) {
4951                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4952                    widget: $this,
4953                    handle: function(content) {
4954                        // do nothing
4955                    }
4956                });
4957
4958                return true;
4959            }
4960        };
4961
4962        PrimeFaces.ajax.Request.handle(options);
4963    },
4964
4965    /**
4966     * Ajax sort
4967     */
4968    sort: function(columnHeader, order, multi) {
4969        var $this = this,
4970        options = {
4971            source: this.id,
4972            update: this.id,
4973            process: this.id,
4974            params: [{name: this.id + '_sorting', value: true},
4975                     {name: this.id + '_skipChildren', value: true},
4976                     {name: this.id + '_encodeFeature', value: true}]
4977        };
4978            
4979        if (!this.cfg.partialUpdate) {
4980            options.params.push({name: this.id + '_fullUpdate', value: true});
4981
4982            options.onsuccess = function(responseXML, status, xhr) {
4983                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4984                        widget: $this,
4985                        handle: function(content) {
4986                            this.jq.replaceWith(content);
4987                        }
4988                    });
4989
4990                return true;
4991            };
4992        }
4993        else {
4994            options.onsuccess = function(responseXML, status, xhr) {
4995                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
4996                        widget: $this,
4997                        handle: function(content) {
4998                            this.updateData(content);
4999
5000                            if(this.checkAllToggler) {
5001                              this.updateHeaderCheckbox();
5002                            }
5003                        }
5004                    });
5005
5006                return true;
5007            };
5008            
5009            options.oncomplete = function(xhr, status, args, data) {
5010                var paginator = $this.getPaginator();
5011                if(args && args.totalRecords) {
5012                    $this.cfg.scrollLimit = args.totalRecords;
5013
5014                    if(paginator && paginator.cfg.rowCount !== args.totalRecords) {
5015                        paginator.setTotalRecords(args.totalRecords);
5016                    }
5017                }
5018
5019                if(!args.validationFailed) {
5020                    if(paginator) {
5021                        paginator.setPage(0, true);
5022                    }
5023
5024                    // remove aria-sort
5025                    var activeColumns = $this.sortableColumns.filter('.ui-state-active');
5026                    if(activeColumns.length) {
5027                        activeColumns.removeAttr('aria-sort');
5028                    }
5029                    else {
5030                        $this.sortableColumns.eq(0).removeAttr('aria-sort');
5031                    }
5032
5033                    if(!multi) {
5034                        //aria reset
5035                        for(var i = 0; i < activeColumns.length; i++) {
5036                            var activeColumn = $(activeColumns.get(i)),
5037                                ariaLabelOfActive = activeColumn.attr('aria-label');
5038
5039                            activeColumn.attr('aria-label', $this.get
5039SortMessage(ariaLabelOfActive, $this.ascMessage));
5040                            $(PrimeFaces.escapeClientId(activeColumn.attr('id') + '_clone')).removeAttr('aria-sort').attr('aria-label', $this.getSortMessage(ariaLabelOfActive, $this.ascMessage));
5041                        }
5042
5043                        activeColumns.data('sortorder', $this.SORT_ORDER.UNSORTED).removeClass('ui-state-active')
5044                                    .find('.ui-sortable-column-icon').removeClass('ui-icon-triangle-1-n ui-icon-triangle-1-s');
5045                    }
5046
5047                    columnHeader.data('sortorder', order).removeClass('ui-state-hover').addClass('ui-state-active');
5048                    var sortIcon = columnHeader.find('.ui-sortable-column-icon'),
5049                    ariaLabel = columnHeader.attr('aria-label');
5050
5051                    if(order === $this.SORT_ORDER.DESCENDING) {
5052                        sortIcon.removeClass('ui-icon-triangle-1-n').addClass('ui-icon-triangle-1-s');
5053                        columnHeader.attr('aria-sort', 'descending').attr('aria-label', $this.getSortMessage(ariaLabel, $this.ascMessage));
5054                        $(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).attr('aria-sort', 'descending').attr('aria-label', $this.getSortMessage(ariaLabel, $this.ascMessage));
5055                    } else if(order === $this.SORT_ORDER.ASCENDING) {
5056                        sortIcon.removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-n');
5057                        columnHeader.attr('aria-sort', 'ascending').attr('aria-label', $this.getSortMessage(ariaLabel, $this.descMessage));
5058                        $(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).attr('aria-sort', 'ascending').attr('aria-label', $this.getSortMessage(ariaLabel, $this.descMessage));
5059                    }
5060                }
5061
5062                if($this.cfg.virtualScroll) {
5063                    $this.resetVirtualScrollBody();
5064                }
5065                else if($this.cfg.liveScroll) {
5066                    $this.scrollOffset = 0;
5067                    $this.liveScrollActive = false;
5068                    $this.shouldLiveScroll = true;
5069                    $this.loadingLiveScroll = false;
5070                    $this.allLoadedLiveScroll = $this.cfg.scrollStep >= $this.cfg.scrollLimit;
5071                }
5072
5073                if($this.cfg.clientCache) {
5074                    $this.clearCacheMap();
5075                }
5076
5077                $this.updateColumnsView();
5078
5079                // reset index of shift selection on multiple mode
5080                $this.originRowIndex = null;
5081            };
5082        }
5083
5084        if(multi) {
5085            options.params.push({name: this.id + '_multiSorting', value: true});
5086            options.params.push({name: this.id + '_sortKey', value: $this.joinSortMetaOption('col')});
5087            options.params.push({name: this.id + '_sortDir', value: $this.joinSortMetaOption('order')});
5088        }
5089        else {
5090            options.params.push({name: this.id + '_sortKey', value: columnHeader.attr('id')});
5091            options.params.push({name: this.id + '_sortDir', value: order});
5092        }
5093
5094        if(this.hasBehavior('sort')) {
5095            this.callBehavior('sort', options);
5096        }
5097        else {
5098            PrimeFaces.ajax.Request.handle(options);
5099        }
5100    },
5101
5102    joinSortMetaOption: function(option) {
5103        var value = '';
5104
5105        for(var i = 0; i < this.sortMeta.length; i++) {
5106            value += this.sortMeta[i][option];
5107
5108            if(i !== (this.sortMeta.length - 1)) {
5109                value += ',';
5110            }
5111        }
5112
5113        return value;
5114    },
5115
5116    /**
5117     * Ajax filter
5118     */
5119    filter: function() {
5120        var $this = this,
5121        options = {
5122            source: this.id,
5123            update: this.id,
5124            process: this.id,
5125            formId: this.cfg.formId,
5126            params: [{name: this.id + '_filtering', value: true},
5127                     {name: this.id + '_encodeFeature', value: true}]
5128        };
5129        
5130        if (!this.cfg.partialUpdate){
5131            options.params.push({name: this.id + '_fullUpdate', value: true});
5132
5133            options.onsuccess = function(responseXML, status, xhr) {
5134                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
5135                        widget: $this,
5136                        handle: function(content) {
5137                            this.jq.replaceWith(content);
5138                        }
5139                    });
5140
5141                return true;
5142            };
5143        }
5144        else {
5145            options.onsuccess = function(responseXML, status, xhr) {
5146                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
5147                        widget: $this,
5148                        handle: function(content) {
5149                            this.updateData(content);
5150
5151                            if(this.cfg.scrollable) {
5152                                this.alignScrollBody();
5153                            }
5154
5155                            if(this.isCheckboxSelectionEnabled()) {
5156                                this.updateHeaderCheckbox();
5157                            }
5158                        }
5159                    });
5160
5161                return true;
5162            };
5163            
5164            options.oncomplete = function(xhr, status, args, data) {
5165                var paginator = $this.getPaginator();
5166                if(args && typeof args.totalRecords !== 'undefined') {
5167                    $this.cfg.scrollLimit = args.totalRecords;
5168
5169                    if(paginator) {
5170                        paginator.setTotalRecords(args.totalRecords);
5171                    }
5172                }
5173
5174                if($this.cfg.clientCache) {
5175                    $this.clearCacheMap();
5176                }
5177
5178                if($this.cfg.virtualScroll) {
5179                    var row = $this.bodyTable.children('tbody').children('tr.ui-widget-content');
5180                    if(row) {
5181                        var hasEmptyMessage = row.eq(0).hasClass('ui-datatable-empty-message'),
5182                        scrollLimit = $this.cfg.scrollLimit;
5183
5184                        if(hasEmptyMessage) {
5185                            scrollLimit = 1;
5186                        }
5187
5188                        $this.resetVirtualScrollBody();
5189
5190                        $this.rowHeight = row.outerHeight();
5191                        $this.scrollBody.children('div').css({'height': parseFloat((scrollLimit * $this.rowHeight + 1) + 'px')});
5192
5193                        if(hasEmptyMessage && $this.cfg.scrollHeight && $this.percentageScrollHeight) {
5194                            setTimeout(function() {
5195                                $this.adjustScrollHeight();
5196                            }, 10);
5197                        }
5198                    }
5199                }
5200                else if($this.cfg.liveScroll) {
5201                    $this.scrollOffset = 0;
5202                    $this.liveScrollActive = false;
5203                    $this.shouldLiveScroll = true;
5204                    $this.loadingLiveScroll = false;
5205                    $this.allLoadedLiveScroll = $this.cfg.scrollStep >= $this.cfg.scrollLimit;
5206                }
5207
5208                $this.updateColumnsView();
5209                $this.updateEmptyColspan();
5210
5211                // reset index of shift selection on multiple mode
5212                $this.originRowIndex = null;
5213            };
5214        }
5215
5216        if(this.hasBehavior('filter')) {
5217            this.callBehavior('filter', options);
5218        }
5219        else {
5220            PrimeFaces.ajax.Request.handle(options);
5221        }
5222    },
5223
5224    onRowClick: function(event, rowElement, silent) {
5225        //Check if rowclick triggered this event not a clickable element in row content
5226        if($(event.target).is(this.rowSelectorForRowClick)) {
5227            var row = $(rowElement),
5228            selected = row.hasClass('ui-state-highlight'),
5229            metaKey = event.metaKey||event.ctrlKey,
5230            shiftKey = event.shiftKey;
5231
5232            this.assignFocusedRow(row);
5233
5234            //unselect a selected row if metakey is on
5235            if(selected && metaKey) {
5236                this.unselectRow(row, silent);
5237            }
5238            else {
5239                //unselect previous selection if this is single selection or multiple one with no keys
5240                if(this.isSingleSelection() || (this.isMultipleSelection() && event && !metaKey && !shiftKey && this.cfg.rowSelectMode === 'new' )) {
5241                    this.unselectAllRows();
5242                }
5243
5244                //range selection with shift key
5245                if(this.isMultipleSelection() && event && event.shiftKey && this.originRowIndex !== null) {
5246                    this.selectRowsInRange(row);
5247                }
5248                else if(this.cfg.rowSelectMode === 'add' && selected) {
5249                    this.unselectRow(row, silent);
5250                }
5251                //select current row
5252                else {
5253                    this.originRowIndex = row.index();
5254                    this.cursorIndex = null;
5255                    this.selectRow(row, silent);
5256                }
5257            }
5258
5259            if(this.cfg.disabledTextSelection) {
5260                PrimeFaces.clearSelection();
5261            }
5262        }
5263    },
5264
5265    onRowDblclick: function(event, row) {
5266        if(this.cfg.disabledTextSelection) {
5267            PrimeFaces.clearSelection();
5268        }
5269
5270        //Check if rowclick triggered this event not a clickable element in row content
5271        if($(event.target).is(this.rowSelectorForRowClick)) {
5272            var rowMeta = this.getRowMeta(row);
5273
5274            this.fireRowSelectEvent(rowMeta.key, 'rowDblselect');
5275        }
5276    },
5277
5278    onRowRightClick: function(event, rowElement, cmSelMode) {
5279        var row = $(rowElement),
5280        rowMeta = this.getRowMeta(row),
5281        selected = row.hasClass('ui-state-highlight');
5282
5283        this.assignFocusedRow(row);
5284
5285        if(cmSelMode === 'single' || !selected) {
5286            this.unselectAllRows();
5287        }
5288
5289        this.selectRow(row, true);
5290
5291        this.fireRowSelectEvent(rowMeta.key, 'contextMenu');
5292
5293        if(this.cfg.disabledTextSelection) {
5294            PrimeFaces.clearSelection();
5295        }
5296    },
5297
5298    /**
5299     * @param r {Row Index || Row Element}
5300     */
5301    findRow: function(r) {
5302        var row = r;
5303
5304        if(PrimeFaces.isNumber(r)) {
5305            row = this.tbody.children('tr:eq(' + r + ')');
5306        }
5307
5308        return row;
5309    },
5310
5311    selectRowsInRange: function(row) {
5312        var rows = this.tbody.children(),
5313        rowMeta = this.getRowMeta(row),
5314        $this = this;
5315
5316        //unselect previously selected rows with shift
5317        if(this.cursorIndex !== null) {
5318            var oldCursorIndex = this.cursorIndex,
5319            rowsToUnselect = oldCursorIndex > this.originRowIndex ? rows.slice(this.originRowIndex, oldCursorIndex + 1) : rows.slice(oldCursorIndex, this.originRowIndex + 1);
5320
5321            rowsToUnselect.each(function(i, item) {
5322                $this.unselectRow($(item), true);
5323            });
5324        }
5325
5326        //select rows between cursor and origin
5327        this.cursorIndex = row.index();
5328
5329        var rowsToSelect = this.cursorIndex > this.originRowIndex ? rows.slice(this.originRowIndex, this.cursorIndex + 1) : rows.slice(this.cursorIndex, this.originRowIndex + 1);
5330
5331        rowsToSelect.each(function(i, item) {
5332            $this.selectRow($(item), true);
5333        });
5334
5335        this.fireRowSelectEvent(rowMeta.key, 'rowSelect');
5336    },
5337
5338    selectRow: function(r, silent) {
5339        var row = this.findRow(r);
5340        if(!row.hasClass('ui-datatable-selectable')) {
5341            return;
5342        }
5343
5344        var rowMeta = this.getRowMeta(row);
5345
5346        this.highlightRow(row);
5347
5348        if(this.isCheckboxSelectionEnabled()) {
5349            if(this.cfg.nativeElements)
5350                row.children('td.ui-selection-column').find(':checkbox').prop('checked', true);
5351            else
5352                this.selectCheckbox(row.children('td.ui-selection-column').find('> div.ui-chkbox > div.ui-chkbox-box'));
5353
5354            this.updateHeaderCheckbox();
5355        }
5356
5357        this.addSelection(rowMeta.key);
5358
5359        this.writeSelections();
5360
5361        if(!silent) {
5362            this.fireRowSelectEvent(rowMeta.key, 'rowSelect');
5363        }
5364    },
5365
5366    unselectRow: function(r, silent) {
5367        var row = this.findRow(r);
5368        if(!row.hasClass('ui-datatable-selectable')) {
5369            return;
5370        }
5371
5372        var rowMeta = this.getRowMeta(row);
5373
5374        this.unhighlightRow(row);
5375
5376        if(this.isCheckboxSelectionEnabled()) {
5377            if(this.cfg.nativeElements)
5378                row.children('td.ui-selection-column').find(':checkbox').prop('checked', false);
5379            else
5380                this.unselectCheckbox(row.children('td.ui-selection-column').find('> div.ui-chkbox > div.ui-chkbox-box'));
5381
5382            this.updateHeaderCheckbox();
5383        }
5384
5385        this.removeSelection(rowMeta.key);
5386
5387        this.writeSelections();
5388
5389        if(!silent) {
5390            this.fireRowUnselectEvent(rowMeta.key, "rowUnselect");
5391        }
5392    },
5393
5394    /*
5395     * Highlights row as selected
5396     */
5397    highlightRow: function(row) {
5398        row.removeClass('ui-state-hover').addClass('ui-state-highlight').attr('aria-selected', true);
5399    },
5400
5401    /*
5402     * Clears selected visuals
5403     */
5404    unhighlightRow: function(row) {
5405        row.removeClass('ui-state-highlight').attr('aria-selected', false);
5406    },
5407
5408    /**
5409     * Sends a rowSelectEvent on server side to invoke a rowSelectListener if defined
5410     */
5411    fireRowSelectEvent: function(rowKey, behaviorEvent) {
5412        if(this.hasBehavior(behaviorEvent)) {
5413            var ext = {
5414                    params: [{name: this.id + '_instantSelectedRowKey', value: rowKey}
5415                ]
5416            };
5417
5418            this.callBehavior(behaviorEvent, ext);
5419        }
5420    },
5421
5422    /**
5423     * Sends a rowUnselectEvent on server side to invoke a rowUnselectListener if defined
5424     */
5425    fireRowUnselectEvent: function(rowKey, behaviorEvent) {
5426        if(this.hasBehavior(behaviorEvent)) {
5427            var ext = {
5428                params: [
5429                {
5430                    name: this.id + '_instantUnselectedRowKey',
5431                    value: rowKey
5432                }
5433                ]
5434            };
5435
5436            this.callBehavior(behaviorEvent, ext);
5437        }
5438    },
5439
5440    /**
5441     * Selects the corresping row of a radio based column selection
5442     */
5443    selectRowWithRadio: function(radio) {
5444        var row = radio.closest('tr'),
5445        rowMeta = this.getRowMeta(row);
5446
5447        //clean selection
5448        this.unselectAllRows();
5449
5450        //select current
5451        if(!this.cfg.nativeElements) {
5452            this.selectRadio(radio);
5453        }
5454
5455        this.highlightRow(row);
5456        this.addSelection(rowMeta.key);
5457        this.writeSelections();
5458        this.fireRowSelectEvent(rowMeta.key, 'rowSelectRadio');
5459    },
5460
5461    /**
5462     * Selects the corresponding row of a checkbox based column selection
5463     */
5464    selectRowWithCheckbox: function(checkbox, silent) {
5465        var row = checkbox.closest('tr');
5466        if(!row.hasClass('ui-datatable-selectable')) {
5467            return;
5468        }
5469
5470        var rowMeta = this.getRowMeta(row);
5471
5472        this.highlightRow(row);
5473
5474        if(!this.cfg.nativeElements) {
5475            this.selectCheckbox(checkbox);
5476        }
5477
5478        this.addSelection(rowMeta.key);
5479
5480        this.writeSelections();
5481
5482        if(!silent) {
5483            this.updateHeaderCheckbox();
5484            this.fireRowSelectEvent(rowMeta.key, "rowSelectCheckbox");
5485        }
5486    },
5487
5488    /**
5489     * Unselects the corresponding row of a checkbox based column selection
5490     */
5491    unselectRowWithCheckbox: function(checkbox, silent) {
5492        var row = checkbox.closest('tr');
5493        if(!row.hasClass('ui-datatable-selectable')) {
5494            return;
5495        }
5496
5497        var rowMeta = this.getRowMeta(row);
5498
5499        this.unhighlightRow(row);
5500
5501        if(!this.cfg.nativeElements) {
5502            this.unselectCheckbox(checkbox);
5503        }
5504
5505        this.removeSelection(rowMeta.key);
5506
5507        this.uncheckHeaderCheckbox();
5508
5509        this.writeSelections();
5510
5511        if(!silent) {
5512            this.fireRowUnselectEvent(rowMeta.key, "rowUnselectCheckbox");
5513        }
5514    },
5515
5516    unselectAllRows: function() {
5517        var selectedRows = this.tbody.children('tr.ui-state-highlight'),
5518        checkboxSelectionEnabled = this.isCheckboxSelectionEnabled(),
5519        radioSelectionEnabled = this.isRadioSelectionEnabled();
5520
5521        for(var i = 0; i < selectedRows.length; i++) {
5522            var row = selectedRows.eq(i);
5523            if(!row.hasClass('ui-datatable-selectable')) {
5524                continue;
5525            }
5526
5527            this.unhighlightRow(row);
5528
5529            if(checkboxSelectionEnabled) {
5530                if(this.cfg.nativeElements)
5531                    row.children('td.ui-selection-column').find(':checkbox').prop('checked', false);
5532                else
5533                    this.unselectCheckbox(row.children('td.ui-selection-column').find('> div.ui-chkbox > div.ui-chkbox-box'));
5534            }
5535            else if(radioSelectionEnabled) {
5536                if(this.cfg.nativeElements)
5537                    row.children('td.ui-selection-column').find(':radio').prop('checked', false);
5538                else
5539                    this.unselectRadio(row.children('td.ui-selection-column').find('> div.ui-radiobutton > div.ui-radiobutton-box'));
5540            }
5541        }
5542
5543        if(checkboxSelectionEnabled) {
5544            this.uncheckHeaderCheckbox();
5545        }
5546
5547        this.selection = [];
5548        this.writeSelections();
5549    },
5550
5551    selectAllRowsOnPage: function() {
5552        var rows = this.tbody.children('tr');
5553        for(var i = 0; i < rows.length; i++) {
5554            var row = rows.eq(i);
5555            this.selectRow(row, true);
5556        }
5557    },
5558
5559    unselectAllRowsOnPage: function() {
5560        var rows = this.tbody.children('tr');
5561        for(var i = 0; i < rows.length; i++) {
5562            var row = rows.eq(i);
5563            this.unselectRow(row, true);
5564        }
5565    },
5566
5567    selectAllRows: function() {
5568        this.selectAllRowsOnPage();
5569        this.selection = new Array('@all');
5570        this.writeSelections();
5571    },
5572
5573    /**
5574     * Toggles all rows with checkbox
5575     */
5576    toggleCheckAll: function() {
5577        var shouldCheckAll = true;
5578        if(this.cfg.nativeElements) {
5579            var checkboxes = this.tbody.find('> tr.ui-datatable-selectable > td.ui-selection-column > :checkbox:visible'),
5580            checked = this.checkAllToggler.prop('checked'),
5581            $this = this;
5582
5583            checkboxes.each(function() {
5584                if(checked) {
5585                    var checkbox = $(this);
5586                    checkbox.prop('checked', true);
5587                    $this.selectRowWithCheckbox(checkbox, true);
5588                }
5589                else {
5590                    var checkbox = $(this);
5591                    checkbox.prop('checked', false);
5592                    $this.unselectRowWithCheckbox(checkbox, true);
5593                    shouldCheckAll = false;
5594                }
5595            });
5596        }
5597        else {
5598            var checkboxes = this.tbody.find('> tr.ui-datatable-selectable > td.ui-selection-column .ui-chkbox-box:visible'),
5599            checked = this.checkAllToggler.hasClass('ui-state-active'),
5600            $this = this;
5601
5602            if(checked) {
5603                this.checkAllToggler.removeClass('ui-state-active').children('span.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
5604                this.checkAllTogglerInput.prop('checked', false).attr('aria-checked', false);
5605                shouldCheckAll = false;
5606
5607                checkboxes.each(function() {
5608                    $this.unselectRowWithCheckbox($(this), true);
5609                });
5610            }
5611            else {
5612                this.checkAllToggler.addClass('ui-state-active').children('span.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
5613                this.checkAllTogglerInput.prop('checked', true).attr('aria-checked', true);
5614
5615                checkboxes.each(function() {
5616                    $this.selectRowWithCheckbox($(this), true);
5617                });
5618            }
5619        }
5620
5621        // GitHub #6730 user wants all rows not just displayed rows
5622        if(!this.cfg.selectionPageOnly && shouldCheckAll) {
5623            this.selectAllRows();
5624        }
5625
5626        //save state
5627        this.writeSelections();
5628
5629        //fire toggleSelect event
5630        if(this.hasBehavior('toggleSelect')) {
5631            var ext = {
5632                params: [{name: this.id + '_checked', value: !checked}]
5633            };
5634
5635            this.callBehavior('toggleSelect', ext);
5636        }
5637    },
5638
5639    selectCheckbox: function(checkbox) {
5640        if(!checkbox.hasClass('ui-state-focus')) {
5641            checkbox.addClass('ui-state-active');
5642        }
5643        checkbox.children('span.ui-chkbox-icon:first').removeClass('ui-icon-blank').addClass(' ui-icon-check');
5644        checkbox.prev().children('input').prop('checked', true).attr('aria-checked', true);
5645    },
5646
5647    unselectCheckbox: function(checkbox) {
5648        checkbox.removeClass('ui-state-active');
5649        checkbox.children('span.ui-chkbox-icon:first').addClass('ui-icon-blank').removeClass('ui-icon-check');
5650        checkbox.prev().children('input').prop('checked', false).attr('aria-checked', false);
5651    },
5652
5653    selectRadio: function(radio){
5654        radio.removeClass('ui-state-hover');
5655        if(!radio.hasClass('ui-state-focus')) {
5656            radio.addClass('ui-state-active');
5657        }
5658        radio.children('.ui-radiobutton-icon').addClass('ui-icon-bullet').removeClass('ui-icon-blank');
5659        radio.prev().children('input').prop('checked', true);
5660    },
5661
5662    unselectRadio: function(radio){
5663        radio.removeClass('ui-state-active').children('.ui-radiobutton-icon').addClass('ui-icon-blank').removeClass('ui-icon-bullet');
5664        radio.prev().children('input').prop('checked', false);
5665    },
5666
5667    /**
5668     * Expands a row to display detail content
5669     */
5670    toggleExpansion: function(toggler) {
5671        var row = toggler.closest('tr'),
5672        rowIndex = this.getRowMeta(row).index,
5673        iconOnly = toggler.hasClass('ui-icon'),
5674        labels = toggler.children('span'),
5675        expanded = iconOnly ? toggler.hasClass('ui-icon-circle-triangle-s'): toggler.children('span').eq(0).hasClass('ui-helper-hidden'),
5676        $this = this;
5677
5678        //Run toggle expansion if row is not being toggled already to prevent conflicts
5679        if($.inArray(rowIndex, this.expansionProcess) === -1) {
5680            this.expansionProcess.push(rowIndex);
5681
5682            if(expanded) {
5683                if(iconOnly) {
5684                    toggler.addClass('ui-icon-circle-triangle-e').removeClass('ui-icon-circle-triangle-s').attr('aria-expanded', false);
5685                }
5686                else {
5687                    labels.eq(0).removeClass('ui-helper-hidden');
5688                    labels.eq(1).addClass('ui-helper-hidden');
5689                }
5690
5691                this.collapseRow(row);
5692                $this.expansionProcess = $.grep($this.expansionProcess, function(r) {
5693                    return (r !== rowIndex);
5694                });
5695                this.fireRowCollapseEvent(row);
5696            }
5697            else {
5698                if(this.cfg.rowExpandMode === 'single') {
5699                    this.collapseAllRows();
5700                }
5701
5702                if(iconOnly) {
5703                    toggler.addClass('ui-icon-circle-triangle-s').removeClass('ui-icon-circle-triangle-e').attr('aria-expanded', true);
5704                }
5705                else {
5706                    labels.eq(0).addClass('ui-helper-hidden');
5707                    labels.eq(1).removeClass('ui-helper-hidden');
5708                }
5709
5710                this.loadExpandedRowContent(row);
5711            }
5712        }
5713    },
5714
5715    loadExpandedRowContent: function(row) {
5716        // To check whether or not any hidden expansion content exists to avoid reloading multiple duplicate nodes in DOM
5717        var expansionContent = row.next('.ui-expanded-row-content');
5718        if(expansionContent.length > 0) {
5719            expansionContent.remove();
5720        }
5721
5722        var $this = this,
5723        rowIndex = this.getRowMeta(row).index,
5724        options = {
5725            source: this.id,
5726            process: this.id,
5727            update: this.id,
5728            formId: this.cfg.formId,
5729            params: [{name: this.id + '_rowExpansion', value: true},
5730                     {name: this.id + '_expandedRowIndex', value: rowIndex},
5731                     {name: this.id + '_encodeFeature', value: true},
5732                     {name: this.id + '_skipChildren', value: true}],
5733            onsuccess: function(responseXML, status, xhr) {
5734                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
5735                        widget: $this,
5736                        handle: function(content) {
5737                            if(content && $.trim(content).length) {
5738                                row.addClass('ui-expanded-row');
5739                                this.displayExpandedRow(row, content);
5740                            }
5741                        }
5742                    });
5743
5744                return true;
5745            },
5746            oncomplete: function() {
5747                $this.expansionProcess = $.grep($this.expansionProcess, function(r) {
5748                    return r !== rowIndex;
5749                });
5750            }
5751        };
5752
5753        if(this.hasBehavior('rowToggle')) {
5754            this.callBehavior('rowToggle', options);
5755        }
5756        else {
5757            PrimeFaces.ajax.Request.handle(options);
5758        }
5759    },
5760
5761    displayExpandedRow: function(row, content) {
5762        row.after(content);
5763        this.updateRowspan(row);
5764        this.updateColspan(row.next());
5765    },
5766
5767    fireRowCollapseEvent: function(row) {
5768        var rowIndex = this.getRowMeta(row).index;
5769
5770        if(this.hasBehavior('rowToggle')) {
5771            var ext = {
5772                params: [
5773                {
5774                    name: this.id + '_collapsedRowIndex',
5775                    value: rowIndex
5776                }
5777                ]
5778            };
5779
5780            this.callBehavior('rowToggle', ext);
5781        }
5782    },
5783
5784    collapseRow: function(row) {
5785        // #942: need to use "hide" instead of "remove" to avoid incorrect form mapping when a row is collapsed
5786        row.removeClass('ui-expanded-row').next('.ui-expanded-row-content').hide();
5787        this.updateRowspan(row);
5788    },
5789
5790    collapseAllRows: function() {
5791        var $this = this;
5792
5793        this.getExpandedRows().each(function() {
5794            var expandedRow = $(this);
5795            $this.collapseRow(expandedRow);
5796
5797            var columns = expandedRow.children('td');
5798            for(var i = 0; i < columns.length; i++) {
5799                var column = columns.eq(i),
5800                toggler = column.children('.ui-row-toggler');
5801
5802                if(toggler.length > 0) {
5803                    if(toggler.hasClass('ui-icon')) {
5804                        toggler.addClass('ui-icon-circle-triangle-e').removeClass('ui-icon-circle-triangle-s');
5805                    }
5806                    else {
5807                        var labels = toggler.children('span');
5808                        labels.eq(0).removeClass('ui-helper-hidden');
5809                        labels.eq(1).addClass('ui-helper-hidden');
5810                    }
5811                    break;
5812                }
5813            }
5814        });
5815    },
5816
5817    getExpandedRows: function() {
5818        return this.tbody.children('.ui-expanded-row');
5819    },
5820
5821    /**
5822     * Binds editor events non-obstrusively
5823     */
5824    bindEditEvents: function() {
5825        var $this = this;
5826        this.cfg.saveOnCellBlur = (this.cfg.saveOnCellBlur === false) ? false : true;
5827
5828        if(this.cfg.editMode === 'row') {
5829            var rowEditorSelector = '> tr > td > div.ui-row-editor > a';
5830
5831            this.tbody.off('click.datatable focus.datatable blur.datatable', rowEditorSelector)
5832                        .on('click.datatable', rowEditorSelector, null, function(e) {
5833                            var element = $(this),
5834                            row = element.closest('tr');
5835
5836                            if(element.hasClass('ui-row-editor-pencil')) {
5837                                $this.switchToRowEdit(row);
5838                                element.hide().siblings().show();
5839                            }
5840                            else if(element.hasClass('ui-row-editor-check')) {
5841                                $this.saveRowEdit(row);
5842                            }
5843                            else if(element.hasClass('ui-row-editor-close')) {
5844                                $this.cancelRowEdit(row);
5845                            }
5846
5847                            e.preventDefault();
5848                        })
5849                        .on('focus.datatable', rowEditorSelector, null, function(e) {
5850                            $(this).addClass('ui-row-editor-outline');
5851                        })
5852                        .on('blur.datatable', rowEditorSelector, null, function(e) {
5853                            $(this).removeClass('ui-row-editor-outline');
5854                        });
5855
5856            // GitHub #433 Allow ENTER to submit ESC to cancel row editor
5857            $(document).off("keydown", "tr.ui-row-editing")
5858                        .on("keydown", "tr.ui-row-editing", function(e) {
5859                            var keyCode = $.ui.keyCode;
5860                            switch (e.which) {
5861                                case keyCode.ENTER:
5862                                    $(this).closest("tr").find(".ui-row-editor-check").click();
5863                                    return false; // prevents executing other event handlers (adding new row to the table)
5864                                case keyCode.ESCAPE:
5865                                    $(this).closest("tr").find(".ui-row-editor-close").click();
5866                                    return false;
5867                                default:
5868                                    break;
5869                }
5870            });
5871        }
5872        else if(this.cfg.editMode === 'cell') {
5873            var originalCellSelector = '> tr > td.ui-editable-column'
5874            cellSelector = this.cfg.cellSeparator || originalCellSelector,
5875            editEvent = (this.cfg.editInitEvent !== 'click') ? this.cfg.editInitEvent + '.datatable-cell click.datatable-cell' : 'click.datatable-cell';
5876
5877            if (this.cfg.cellSeparator) {
5878                this.tbody.off(editEvent, originalCellSelector)
5879                    .on(editEvent, originalCellSelector, null, function (e) {
5880                        $this.incellClick = true;
5881
5882                        if (!$(this).hasClass('ui-cell-editing') && e.type === $this.cfg.editInitEvent && $this.cfg.editInitEvent === "dblclick") {
5883                            $this.incellClick = false;
5884                        }
5885                    });
5886            }
5887
5888            this.tbody.off(editEvent, cellSelector)
5889                        .on(editEvent, cellSelector, null, function(e) {
5890                            $this.incellClick = true;
5891
5892                            var item = $(this),
5893                            cell = item.hasClass('ui-editable-column') ? item : item.closest('.ui-editable-column');
5894
5895                            if(!cell.hasClass('ui-cell-editing') && e.type === $this.cfg.editInitEvent) {
5896                                $this.showCellEditor(cell);
5897
5898                                if($this.cfg.editInitEvent === "dblclick") {
5899                                    $this.incellClick = false;
5900                                }
5901                            }
5902                        });
5903
5904            $(document).off('click.datatable-cell-blur' + this.id)
5905                        .on('click.datatable-cell-blur' + this.id, function(e) {
5906                            var target = $(e.target);
5907                            if(!$this.incellClick && (target.is('.ui-input-overlay') || target.closest('.ui-input-overlay').length || target.closest('.ui-datepicker-buttonpane').length)) {
5908                                $this.incellClick = true;
5909                            }
5910
5911                            if(!$this.incellClick && $this.currentCell && !$this.contextMenuClick && !$.datepicker._datepickerShowing) {
5912                                if($this.cfg.saveOnCellBlur)
5913                                    $this.saveCell($this.currentCell);
5914                                else
5915                                    $this.doCellEditCancelRequest($this.currentCell);
5916                            }
5917
5918                            $this.incellClick = false;
5919                            $this.contextMenuClick = false;
5920                        });
5921        }
5922    },
5923
5924    switchToRowEdit: function(row) {
5925        if(this.cfg.rowEditMode === "lazy") {
5926            this.lazyRowEditInit(row);
5927        }
5928        else {
5929            this.showRowEditors(row);
5930
5931            if(this.hasBehavior('rowEditInit')) {
5932                var rowIndex = this.getRowMeta(row).index;
5933
5934                var ext = {
5935                    params: [{name: this.id + '_rowEditIndex', value: rowIndex}]
5936                };
5937
5938                this.callBehavior('rowEditInit', ext);
5939            }
5940        }
5941    },
5942
5943    showRowEditors: function(row) {
5944        row.addClass('ui-state-highlight ui-row-editing').children('td.ui-editable-column').each(function() {
5945            var column = $(this);
5946
5947            column.find('.ui-cell-editor-output').hide();
5948            column.find('.ui-cell-editor-input').show();
5949        });
5950
5951        var inputs=row.find(':input:enabled');
5952        if (inputs.length > 0) {
5953            inputs.first().focus();
5954        }
5955    },
5956
5957    getCellMeta: function(cell) {
5958        var rowMeta = this.getRowMeta(cell.closest('tr')),
5959            cellIndex = cell.index();
5960
5961        if(this.cfg.scrollable && this.cfg.frozenColumns) {
5962            cellIndex = (this.scrollTbody.is(cell.closest('tbody'))) ? (cellIndex + $this.cfg.frozenColumns) : cellIndex;
5963        }
5964
5965        var cellInfo = rowMeta.index + ',' + cellIndex;
5966        if(rowMeta.key) {
5967            cellInfo = cellInfo + ',' + rowMeta.key;
5968        }
5969
5970        return cellInfo;
5971    },
5972
5973    cellEditInit: function(cell) {
5974        var cellInfo = this.getCellMeta(cell),
5975        cellEditor = cell.children('.ui-cell-editor'),
5976        $this = this;
5977
5978        var options = {
5979            source: this.id,
5980            process: this.id,
5981            update: this.id,
5982            global: false,
5983            params: [{name: this.id + '_encodeFeature', value: true},
5984                    {name: this.id + '_cellEditInit', value: true},
5985                    {name: this.id + '_cellInfo', value: cellInfo}],
5986            onsuccess: function(responseXML, status, xhr) {
5987                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
5988                        widget: $this,
5989                        handle: function(content) {
5990                            cellEditor.children('.ui-cell-editor-input').html(content);
5991                        }
5992                    });
5993
5994                return true;
5995            },
5996            oncomplete: function(xhr, status, args, data) {
5997                cell.data('edit-events-bound', false);
5998                $this.showCurrentCell(cell);
5999            }
6000        };
6001
6002        if(this.hasBehavior('cellEditInit')) {
6003            this.callBehavior('cellEditInit', options);
6004        }
6005        else {
6006            PrimeFaces.ajax.Request.handle(options);
6007        }
6008    },
6009
6010    showCellEditor: function(c) {
6011        this.incellClick = true;
6012
6013        var cell = null;
6014
6015        if(c) {
6016            cell = c;
6017
6018            //remove contextmenu selection highlight
6019            if(this.contextMenuCell) {
6020                this.contextMenuCell.parent().removeClass('ui-state-highlight');
6021            }
6022        }
6023        else {
6024            cell = this.contextMenuCell;
6025        }
6026
6027        var editorInput = cell.find('> .ui-cell-editor > .ui-cell-editor-input');
6028        if(editorInput.length !== 0 && editorInput.children().length === 0 && this.cfg.editMode === 'cell') {
6029            // for lazy cellEditMode
6030            this.cellEditInit(cell);
6031        }
6032        else {
6033            this.showCurrentCell(cell);
6034
6035            if(this.hasBehavior('cellEditInit')) {
6036                var cellInfo = this.getCellMeta(cell);
6037                var ext = {
6038                    params: [{name: this.id + '_cellInfo', value: cellInfo}]
6039                };
6040                this.callBehavior('cellEditInit', ext);
6041            }
6042        }
6043    },
6044
6045    showCurrentCell: function(cell) {
6046        var $this = this;
6047
6048        if(this.currentCell) {
6049            if(this.cfg.saveOnCellBlur)
6050                this.saveCell(this.currentCell);
6051            else if(!this.currentCell.is(cell))
6052                this.doCellEditCancelRequest(this.currentCell);
6053        }
6054
6055        if(cell && cell.length) {
6056            this.currentCell = cell;
6057
6058            var cellEditor = cell.children('div.ui-cell-editor'),
6059            displayContainer = cellEditor.children('div.ui-cell-editor-output'),
6060            inputContainer = cellEditor.children('div.ui-cell-editor-input'),
6061            inputs = inputContainer.find(':input:enabled'),
6062            multi = inputs.length > 1;
6063
6064            cell.addClass('ui-state-highlight ui-cell-editing');
6065            displayContainer.hide();
6066            inputContainer.show();
6067            inputs.eq(0).focus().select();
6068
6069            //metadata
6070            if(multi) {
6071                var oldValues = [];
6072                for(var i = 0; i < inputs.length; i++) {
6073                    var input = inputs.eq(i);
6074
6075                    if(input.is(':checkbox')) {
6076                        oldValues.push(input.val() + "_" + input.is(':checked'));
6077                    }
6078                    else {
6079                        oldValues.push(input.val());
6080                    }
6081                }
6082
6083                cell.data('multi-edit', true);
6084                cell.data('old-value', oldValues);
6085            }
6086            else {
6087                cell.data('multi-edit', false);
6088                cell.data('old-value', inputs.eq(0).val());
6089            }
6090
6091            //bind events on demand
6092            if(!cell.data('edit-events-bound')) {
6093                cell.data('edit-events-bound', true);
6094
6095                inputs.on('keydown.datatable-cell', function(e) {
6096                        var keyCode = $.ui.keyCode,
6097                        shiftKey = e.shiftKey,
6098                        key = e.which,
6099                        input = $(this);
6100
6101                        if(key === keyCode.ENTER) {
6102                            $this.saveCell(cell);
6103                            $this.currentCell = null;
6104
6105                            e.preventDefault();
6106                        }
6107                        else if(key === keyCode.TAB) {
6108                            if(multi) {
6109                                var focusIndex = shiftKey ? input.index() - 1 : input.index() + 1;
6110
6111                                if(focusIndex < 0 || (focusIndex === inputs.length) || input.parent().hasClass('ui-inputnumber') || input.parent().hasClass('ui-helper-hidden-accessible')) {
6112                                    $this.tabCell(cell, !shiftKey);
6113                                } else {
6114                                    inputs.eq(focusIndex).focus();
6115                                }
6116                            }
6117                            else {
6118                                $this.tabCell(cell, !shiftKey);
6119                            }
6120
6121                            e.preventDefault();
6122                        }
6123                        else if(key === keyCode.ESCAPE) {
6124                            $this.doCellEditCancelRequest(cell);
6125                            e.preventDefault();
6126                        }
6127                    })
6128                    .on('focus.datatable-cell click.datatable-cell', function(e) {
6129                        $this.currentCell = cell;
6130                    });
6131            }
6132        }
6133        else {
6134            this.currentCell = null;
6135        }
6136    },
6137
6138    tabCell: function(cell, forward) {
6139        var targetCell = forward ? cell.nextAll('td.ui-editable-column:first') : cell.prevAll('td.ui-editable-column:first');
6140        if(targetCell.length == 0) {
6141            var tabRow = forward ? cell.parent().next() : cell.parent().prev();
6142            targetCell = forward ? tabRow.children('td.ui-editable-column:first') : tabRow.children('td.ui-editable-column:last');
6143        }
6144
6145        var cellEditor = targetCell.children('div.ui-cell-editor'),
6146        inputContainer = cellEditor.children('div.ui-cell-editor-input');
6147
6148        if(inputContainer.length) {
6149            var inputs = inputContainer.find(':input'),
6150            disabledInputs = inputs.filter(':disabled');
6151
6152            if(inputs.length === disabledInputs.length) {
6153                this.tabCell(targetCell, forward);
6154                return;
6155            }
6156        }
6157
6158        this.showCellEditor(targetCell);
6159    },
6160
6161    saveCell: function(cell) {
6162        var inputs = cell.find('div.ui-cell-editor-input :input:enabled'),
6163        changed = false,
6164        valid = cell.data('valid'),
6165        $this = this;
6166
6167        if(cell.data('multi-edit')) {
6168            var oldValues = cell.data('old-value');
6169            for(var i = 0; i < inputs.length; i++) {
6170                var input = inputs.eq(i),
6171                    inputVal = input.val(),
6172                    oldValue = oldValues[i];
6173
6174                if(input.is(':checkbox') || input.is(':radio')) {
6175                    inputVal = inputVal + "_" + input.is(':checked');
6176                }
6177
6178                if(inputVal != oldValue) {
6179                    changed = true;
6180                    break;
6181                }
6182            }
6183        }
6184        else {
6185            var input = inputs.eq(0),
6186                inputVal = input.val(),
6187                oldValue = cell.data('old-value');
6188
6189            if(input.is(':checkbox') || input.is(':radio')) {
6190                inputVal = inputVal + "_" + input.is(':checked');
6191            } 
6192            changed = (inputVal != oldValue);
6193        }
6194
6195        if(changed || valid == false)
6196            $this.doCellEditRequest(cell);
6197        else
6198            $this.viewMode(cell);
6199
6200        if(this.cfg.saveOnCellBlur) {
6201            this.currentCell = null;
6202        }
6203    },
6204
6205    viewMode: function(cell) {
6206        var cellEditor = cell.children('div.ui-cell-editor'),
6207        editableContainer = cellEditor.children('div.ui-cell-editor-input'),
6208        displayContainer = cellEditor.children('div.ui-cell-editor-output');
6209
6210        cell.removeClass('ui-cell-editing ui-state-error ui-state-highlight');
6211        displayContainer.show();
6212        editableContainer.hide();
6213        cell.removeData('old-value').removeData('multi-edit');
6214
6215        if(this.cfg.cellEditMode === "lazy") {
6216            editableContainer.children().remove();
6217        }
6218    },
6219
6220    doCellEditRequest: function(cell) {
6221        var rowMeta = this.getRowMeta(cell.closest('tr')),
6222        cellEditor = cell.children('.ui-cell-editor'),
6223        cellEditorId = cellEditor.attr('id'),
6224        cellIndex = cell.index(),
6225        $this = this;
6226
6227        if(this.cfg.scrollable && this.cfg.frozenColumns) {
6228            cellIndex = (this.scrollTbody.is(cell.closest('tbody'))) ? (cellIndex + $this.cfg.frozenColumns) : cellIndex;
6229        }
6230
6231        var cellInfo = rowMeta.index + ',' + cellIndex;
6232        if(rowMeta.key) {
6233            cellInfo = cellInfo + ',' + rowMeta.key;
6234        }
6235
6236        var options = {
6237            source: this.id,
6238            process: this.id,
6239            update: this.id,
6240            params: [{name: this.id + '_encodeFeature', value: true},
6241                     {name: this.id + '_cellInfo', value: cellInfo},
6242                     {name: cellEditorId, value: cellEditorId}],
6243            onsuccess: function(responseXML, status, xhr) {
6244                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
6245                        widget: $this,
6246                        handle: function(content) {
6247                            cellEditor.children('.ui-cell-editor-output').html(content);
6248                        }
6249                    });
6250
6251                return true;
6252            },
6253            oncomplete: function(xhr, status, args, data) {
6254                if(args.validationFailed){
6255                    cell.data('valid', false);
6256                    cell.addClass('ui-state-error');
6257                }
6258                else{
6259                    cell.data('valid', true);
6260                    $this.viewMode(cell);
6261                }
6262
6263                if($this.cfg.clientCache) {
6264                    $this.clearCacheMap();
6265                }
6266            }
6267        };
6268
6269        if(this.hasBehavior('cellEdit')) {
6270            this.callBehavior('cellEdit', options);
6271        }
6272        else {
6273            PrimeFaces.ajax.Request.handle(options);
6274        }
6275    },
6276
6277    doCellEditCancelRequest: function(cell) {
6278        var rowMeta = this.getRowMeta(cell.closest('tr')),
6279        cellEditor = cell.children('.ui-cell-editor'),
6280        cellIndex = cell.index(),
6281        $this = this;
6282
6283        if(this.cfg.scrollable && this.cfg.frozenColumns) {
6284            cellIndex = (this.scrollTbody.is(cell.closest('tbody'))) ? (cellIndex + $this.cfg.frozenColumns) : cellIndex;
6285        }
6286
6287        var cellInfo = rowMeta.index + ',' + cellIndex;
6288        if(rowMeta.key) {
6289            cellInfo = cellInfo + ',' + rowMeta.key;
6290        }
6291
6292        this.currentCell = null;
6293
6294        var options = {
6295            source: this.id,
6296            process: this.id,
6297            update: this.id,
6298            params: [{name: this.id + '_encodeFeature', value: true},
6299                     {name: this.id + '_cellEditCancel', value: true},
6300                     {name: this.id + '_cellInfo', value: cellInfo}],
6301            onsuccess: function(responseXML, status, xhr) {
6302                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
6303                        widget: $this,
6304                        handle: function(content) {
6305                            cellEditor.children('.ui-cell-editor-input').html(content);
6306                        }
6307                    });
6308
6309                return true;
6310            },
6311            oncomplete: function(xhr, status, args, data) {
6312                $this.viewMode(cell);
6313                cell.data('edit-events-bound', false);
6314
6315                if($this.cfg.clientCache) {
6316                    $this.clearCacheMap();
6317                }
6318            }
6319        };
6320
6321        if(this.hasBehavior('cellEditCancel')) {
6322            this.callBehavior('cellEditCancel', options);
6323        }
6324        else {
6325            PrimeFaces.ajax.Request.handle(options);
6326        }
6327    },
6328
6329    /**
6330     * Saves the edited row
6331     */
6332    saveRowEdit: function(rowEditor) {
6333        this.doRowEditRequest(rowEditor, 'save');
6334    },
6335
6336    /**
6337     * Cancels row editing
6338     */
6339    cancelRowEdit: function(rowEditor) {
6340        this.doRowEditRequest(rowEditor, 'cancel');
6341    },
6342
6343    /**
6344     * Sends an ajax request to handle row save or cancel
6345     */
6346    doRowEditRequest: function(rowEditor, action) {
6347        var row = rowEditor.closest('tr'),
6348        rowIndex = this.getRowMeta(row).index,
6349        expanded = row.hasClass('ui-expanded-row'),
6350        $this = this,
6351        options = {
6352            source: this.id,
6353            process: this.id,
6354            update: this.id,
6355            formId: this.cfg.formId,
6356            params: [{name: this.id + '_rowEditIndex', value: this.getRowMeta(row).index},
6357                     {name: this.id + '_rowEditAction', value: action},
6358                     {name: this.id + '_encodeFeature', value: true}],
6359            onsuccess: function(responseXML, status, xhr) {
6360                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
6361                        widget: $this,
6362                        handle: function(content) {
6363                            if(expanded) {
6364                                this.collapseRow(row);
6365                            }
6366
6367                            this.updateRow(row, content);
6368                        }
6369                    });
6370
6371                return true;
6372            },
6373            oncomplete: function(xhr, status, args, data) {
6374                if(args && args.validationFailed) {
6375                    $this.invalidateRow(rowIndex);
6376                }
6377                else {
6378                    if($this.cfg.rowEditMode === "lazy") {
6379                        var index = ($this.paginator) ? (rowIndex % $this.paginator.getRows()) : rowIndex,
6380                        newRow = $this.tbody.children('tr').eq(index);
6381                        $this.getRowEditors(newRow).children('.ui-cell-editor-input').children().remove();
6382                    }
6383                }
6384
6385                if($this.cfg.clientCache) {
6386                    $this.clearCacheMap();
6387                }
6388            }
6389        };
6390
6391        if(action === 'save') {
6392            this.getRowEditors(row).each(function() {
6393                options.params.push({name: this.id, value: this.id});
6394            });
6395        }
6396
6397        if(action === 'save' && this.hasBehavior('rowEdit')) {
6398            this.callBehavior('rowEdit', options);
6399        }
6400        else if(action === 'cancel' && this.hasBehavior('rowEditCancel')) {
6401            this.callBehavior('rowEditCancel', options);
6402        }
6403        else {
6404            PrimeFaces.ajax.Request.handle(options);
6405        }
6406    },
6407
6408    lazyRowEditInit: function(row) {
6409        var rowIndex = this.getRowMeta(row).index,
6410        $this = this;
6411
6412        var options = {
6413            source: this.id,
6414            process: this.id,
6415            update: this.id,
6416            global: false,
6417            params: [{name: this.id + '_encodeFeature', value: true},
6418                    {name: this.id + '_rowEditInit', value: true},
6419                    {name: this.id + '_rowEditIndex', value: rowIndex}],
6420            onsuccess: function(responseXML, status, xhr) {
6421                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
6422                        widget: $this,
6423                        handle: function(content) {
6424                            $this.updateRow(row, content);
6425                        }
6426                    });
6427
6428                return true;
6429            },
6430            oncomplete: function(xhr, status, args, data) {
6431                var index = ($this.paginator) ? (rowIndex % $this.paginator.getRows()) : rowIndex,
6432                newRow = $this.tbody.children('tr').eq(index);
6433                $this.showRowEditors(newRow);
6434            }
6435        };
6436
6437        if(this.hasBehavior('rowEditInit')) {
6438            this.cfg.behaviors['rowEditInit'].call(this, options);
6439        }
6440        else {
6441            PrimeFaces.ajax.Request.handle(options);
6442        }
6443    },
6444
6445    /*
6446     * Updates row with given content
6447     */
6448    updateRow: function(row, content) {
6449        row.replaceWith(content);
6450    },
6451
6452    /**
6453     * Displays row editors in invalid format
6454     */
6455    invalidateRow: function(index) {
6456        var i = (this.paginator) ? (index % this.paginator.getRows()) : index;
6457        this.tbody.children('tr[data-ri]').eq(i).addClass('ui-widget-content ui-row-editing ui-state-error');
6458    },
6459
6460    /**
6461     * Finds all editors of a row
6462     */
6463    getRowEditors: function(row) {
6464        return row.find('div.ui-cell-editor');
6465    },
6466
6467    /**
6468     * Returns the paginator instance if any defined
6469     */
6470    getPaginator: function() {
6471        return this.paginator;
6472    },
6473
6474    /**
6475     * Writes selected row ids to state holder
6476     */
6477    writeSelections: function() {
6478        $(this.selectionHolder).val(this.selection.join(','));
6479    },
6480
6481    isSingleSelection: function() {
6482        return this.cfg.selectionMode == 'single';
6483    },
6484
6485    isMultipleSelection: function() {
6486        return this.cfg.selectionMode == 'multiple' || this.isCheckboxSelectionEnabled();
6487    },
6488
6489    /**
6490     * Clears the selection state
6491     */
6492    clearSelection: function() {
6493        this.selection = [];
6494
6495        $(this.selectionHolder).val('');
6496    },
6497
6498    /**
6499     * Returns true|false if selection is enabled|disabled
6500     */
6501    isSelectionEnabled: function() {
6502        return this.cfg.selectionMode != undefined || this.cfg.columnSelectionMode != undefined;
6503    },
6504
6505    /**
6506     * Returns true|false if checkbox selection is enabled|disabled
6507     */
6508    isCheckboxSelectionEnabled: function() {
6509        return this.cfg.selectionMode === 'checkbox';
6510    },
6511
6512    /**
6513     * Returns true|false if radio selection is enabled|disabled
6514     */
6515    isRadioSelectionEnabled: function() {
6516        return this.cfg.selectionMode === 'radio';
6517    },
6518
6519    /**
6520     * Clears table filters
6521     */
6522    clearFilters: function() {
6523        this.thead.find('> tr > th.ui-filter-column > .ui-column-filter').val('');
6524        $(this.jqId + '\\:globalFilter').val('');
6525
6526        this.filter();
6527    },
6528
6529    setupResizableColumns: function() {
6530        this.cfg.resizeMode = this.cfg.resizeMode||'fit';
6531
6532        this.fixColumnWidths();
6533
6534        this.hasColumnGroup = this.hasColGroup();
6535        if(this.hasColumnGroup) {
6536            this.addGhostRow();
6537        }
6538
6539        if(!this.cfg.liveResize) {
6540            this.resizerHelper = $('<div class="ui-column-resizer-helper ui-state-highlight"></div>').appendTo(this.jq);
6541        }
6542
6543        this.addResizers();
6544
6545        var resizers = this.thead.find('> tr > th > span.ui-column-resizer'),
6546        $this = this;
6547
6548        resizers.draggable({
6549            axis: 'x',
6550            start: function(event, ui) {
6551                ui.helper.data('originalposition', ui.helper.offset());
6552
6553                if($this.cfg.liveResize) {
6554                    $this.jq.css('cursor', 'col-resize');
6555                }
6556                else {
6557                    var header = $this.cfg.stickyHeader ? $this.clone : $this.thead,
6558                        height = $this.cfg.scrollable ? $this.scrollBody.height() : header.parent().height() - header.height() - 1;
6559
6560                    if($this.cfg.stickyHeader) {
6561                        height = height - $this.relativeHeight;
6562                    }
6563
6564                    $this.resizerHelper.height(height);
6565                    $this.resizerHelper.show();
6566                }
6567            },
6568            drag: function(event, ui) {
6569                if($this.cfg.liveResize) {
6570                    $this.resize(event, ui);
6571                }
6572                else {
6573                    $this.resizerHelper.offset({
6574                        left: ui.helper.offset().left + ui.helper.width() / 2,
6575                        top: $this.thead.offset().top + $this.thead.height()
6576                    });
6577                }
6578            },
6579            stop: function(event, ui) {
6580                ui.helper.css({
6581                    'left': '',
6582                    'top': '0px'
6583                });
6584
6585                if($this.cfg.liveResize) {
6586                    $this.jq.css('cursor', 'default');
6587                } else {
6588                    $this.resize(event, ui);
6589                    $this.resizerHelper.hide();
6590                }
6591
6592                if($this.cfg.resizeMode === 'expand') {
6593                    setTimeout(function() {
6594                        $this.fireColumnResizeEvent(ui.helper.parent());
6595                    }, 5);
6596                }
6597                else {
6598                    $this.fireColumnResizeEvent(ui.helper.parent());
6599                }
6600
6601                if($this.cfg.stickyHeader) {
6602                    $this.reclone();
6603                }
6604            },
6605            containment: this.cfg.resizeMode === "expand" ? "document" : this.jq
6606        });
6607    },
6608
6609    fireColumnResizeEvent: function(columnHeader) {
6610        if(this.hasBehavior('colResize')) {
6611            var options = {
6612                source: this.id,
6613                process: this.id,
6614                params: [
6615                    {name: this.id + '_colResize', value: true},
6616                    {name: this.id + '_columnId', value: columnHeader.attr('id')},
6617                    {name: this.id + '_width', value: parseInt(columnHeader.width())},
6618                    {name: this.id + '_height', value: parseInt(columnHeader.height())}
6619                ]
6620            };
6621
6622            this.callBehavior('colResize', options);
6623        }
6624    },
6625
6626    hasColGroup: function() {
6627        return this.thead.children('tr').length > 1;
6628    },
6629
6630    addGhostRow: function() {
6631        var firstRow = this.tbody.find('tr:first');
6632        if(firstRow.hasClass('ui-datatable-empty-message')) {
6633            return;
6634        }
6635
6636        var columnsOfFirstRow = firstRow.children('td'),
6637        dataColumnsCount = columnsOfFirstRow.length,
6638        columnMarkup = '';
6639
6640        for(var i = 0; i < dataColumnsCount; i++) {
6641            var colWidth = columnsOfFirstRow.eq(i).width() + 1,
6642            id = this.id + '_ghost_' + i;
6643
6644            if(this.cfg.multiViewState && this.resizableStateHolder.attr('value')) {
6645                colWidth = (this.findColWidthInResizableState(id) || colWidth);
6646            }
6647
6648            columnMarkup += '<th id="' + id + '" style="height:0px;border-bottom-width: 0px;border-top-width: 0px;padding-top: 0px;padding-bottom: 0px;outline: 0 none; width:' + colWidth + 'px" class="ui-resizable-column"></th>';
6649        }
6650
6651        this.thead.prepend('<tr>' + columnMarkup + '</tr>');
6652
6653        if(this.cfg.scrollable) {
6654            this.theadClone.prepend('<tr>' + columnMarkup + '</tr>');
6655            this.footerTable.children('tfoot').prepend('<tr>' + columnMarkup + '</tr>');
6656        }
6657    },
6658
6659    findGroupResizer: function(ui) {
6660        for(var i = 0; i < this.groupResizers.length; i++) {
6661            var groupResizer = this.groupResizers.eq(i);
6662            if(groupResizer.offset().left === ui.helper.data('originalposition').left) {
6663                return groupResizer;
6664            }
6665        }
6666
6667        return null;
6668    },
6669
6670    addResizers: function() {
6671        var resizableColumns = this.thead.find('> tr > th.ui-resizable-column');
6672        resizableColumns.prepend('<span class="ui-column-resizer">&nbsp;</span>');
6673
6674        if(this.cfg.resizeMode === 'fit') {
6675            resizableColumns.filter(':last-child').children('span.ui-column-resizer').hide();
6676        }
6677
6678        if(this.hasColumnGroup) {
6679            this.groupResizers = this.thead.find('> tr:first > th > .ui-column-resizer');
6680        }
6681    },
6682
6683    resize: function(event, ui) {
6684        var columnHeader, nextColumnHeader, change = null, newWidth = null, nextColumnWidth = null,
6685        expandMode = (this.cfg.resizeMode === 'expand'),
6686        table = this.thead.parent(),
6687        $this = this;
6688
6689        if(this.hasColumnGroup) {
6690            var groupResizer = this.findGroupResizer(ui);
6691            if(!groupResizer) {
6692                return;
6693            }
6694
6695            columnHeader = groupResizer.parent();
6696        }
6697        else {
6698            columnHeader = ui.helper.parent();
6699        }
6700
6701        var title = columnHeader.children('.ui-column-title');
6702        if(PrimeFaces.env.isIE()) {
6703            title.css('display', 'none');
6704        }
6705
6706        var nextColumnHeader = columnHeader.nextAll(':visible:first');
6707
6708        if(this.cfg.liveResize) {
6709            change = columnHeader.outerWidth() - (event.pageX - columnHeader.offset().left),
6710            newWidth = (columnHeader.width() - change),
6711            nextColumnWidth = (nextColumnHeader.width() + change);
6712        }
6713        else {
6714            change = (ui.position.left - ui.originalPosition.left),
6715            newWidth = (columnHeader.width() + change),
6716            nextColumnWidth = (nextColumnHeader.width() - change);
6717        }
6718
6719        var minWidth = parseInt(columnHeader.css('min-width'));
6720        minWidth = (minWidth == 0) ? 15 : minWidth;
6721
6722        if(PrimeFaces.env.isIE()) {
6723            title.css('display', '');
6724        }
6725
6726        if((newWidth > minWidth && nextColumnWidth > minWidth) || (expandMode && newWidth > minWidth)) {
6727            if(expandMode) {
6728                table.width(table.width() + change);
6729                setTimeout(function() {
6730                    columnHeader.width(newWidth);
6731                    $this.updateResizableState(columnHeader, nextColumnHeader, table, newWidth, null);
6732                }, 1);
6733            }
6734            else {
6735                columnHeader.width(newWidth);
6736                nextColumnHeader.width(nextColumnWidth);
6737                this.updateResizableState(columnHeader, nextColumnHeader, table, newWidth, nextColumnWidth);
6738            }
6739
6740            if(this.cfg.scrollable) {
6741                var cloneTable = this.theadClone.parent(),
6742                colIndex = columnHeader.index();
6743
6744                if(expandMode) {
6745                    //body
6746                    cloneTable.width(cloneTable.width() + change);
6747
6748                    //footer
6749                    this.footerTable.width(this.footerTable.width() + change);
6750
6751                    setTimeout(function() {
6752                        if($this.hasColumnGroup) {
6753                            $this.theadClone.find('> tr:first').children('th').eq(colIndex).width(newWidth);            //body
6754                            $this.footerTable.find('> tfoot > tr:first').children('th').eq(colIndex).width(newWidth);   //footer
6755                        }
6756                        else {
6757                            $this.theadClone.find(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).width(newWidth);   //body
6758                            $this.footerCols.eq(colIndex).width(newWidth);                                                          //footer
6759                        }
6760                    }, 1);
6761                }
6762                else {
6763                    if(this.hasColumnGroup) {
6764                        //body
6765                        this.theadClone.find('> tr:first').children('th').eq(colIndex).width(newWidth);
6766                        this.theadClone.find('> tr:first').children('th').eq(colIndex + 1).width(nextColumnWidth);
6767
6768                        //footer
6769                        this.footerTable.find('> tfoot > tr:first').children('th').eq(colIndex).width(newWidth);
6770                        this.footerTable.find('> tfoot > tr:first').children('th').eq(colIndex + 1).width(nextColumnWidth);
6771                    }
6772                    else {
6773                        //body
6774                        this.theadClone.find(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).width(newWidth);
6775                        this.theadClone.find(PrimeFaces.escapeClientId(nextColumnHeader.attr('id') + '_clone')).width(nextColumnWidth);
6776
6777                        //footer
6778                        if(this.footerCols.length > 0) {
6779                            var footerCol = this.footerCols.eq(colIndex),
6780                            nextFooterCol = footerCol.next();
6781
6782                            footerCol.width(newWidth);
6783                            nextFooterCol.width(nextColumnWidth);
6784                        }
6785                    }
6786                }
6787            }
6788        }
6789    },
6790
6791    /**
6792     * Remove given rowIndex from selection
6793     */
6794    removeSelection: function(rowIndex) {
6795        this.selection = $.grep(this.selection, function(value) {
6796            return value != rowIndex;
6797        });
6798    },
6799
6800    /**
6801     * Adds given rowKey to selection if it doesn't exist already
6802     */
6803    addSelection: function(rowKey) {
6804        if(!this.isSelected(rowKey)) {
6805            this.selection.push(rowKey);
6806        }
6807    },
6808
6809    /**
6810     * Finds if given rowKey is in selection
6811     */
6812    isSelected: function(rowKey) {
6813        return PrimeFaces.inArray(this.selection, rowKey);
6814    },
6815
6816    getRowMeta: function(row) {
6817        var meta = {
6818            index: row.data('ri'),
6819            key:  row.attr('data-rk')
6820        };
6821
6822        return meta;
6823    },
6824
6825    setupDraggableColumns: function() {
6826        this.orderStateHolder = $(this.jqId + '_columnOrder');
6827        this.saveColumnOrder();
6828
6829        this.dragIndicatorTop = $('<span class="ui-icon ui-icon-arrowthick-1-s" style="position:absolute"></span>').hide().appendTo(this.jq);
6830        this.dragIndicatorBottom = $('<span class="ui-icon ui-icon-arrowthick-1-n" style="position:absolute"></span>').hide().appendTo(this.jq);
6831
6832        var $this = this;
6833
6834        $(this.jqId + ' thead th').draggable({
6835            appendTo: 'body',
6836            opacity: 0.75,
6837            cursor: 'move',
6838            scope: this.id,
6839            cancel: ':input,.ui-column-resizer',
6840            start: function(event, ui) {
6841                ui.helper.css('z-index', ++PrimeFaces.zindex);
6842            },
6843            drag: function(event, ui) {
6844                var droppable = ui.helper.data('droppable-column');
6845
6846                if(droppable) {
6847                    var droppableOffset = droppable.offset(),
6848                    topArrowY = droppableOffset.top - 10,
6849                    bottomArrowY = droppableOffset.top + droppable.height() + 8,
6850                    arrowX = null;
6851
6852                    //calculate coordinates of arrow depending on mouse location
6853                    if(event.originalEvent.pageX >= droppableOffset.left + (droppable.width() / 2)) {
6854                        var nextDroppable = droppable.next();
6855                        if(nextDroppable.length == 1)
6856                            arrowX = nextDroppable.offset().left - 9;
6857                        else
6858                            arrowX = droppable.offset().left + droppable.
6858innerWidth() - 9;
6859
6860                        ui.helper.data('drop-location', 1);     //right
6861                    }
6862                    else {
6863                        arrowX = droppableOffset.left  - 9;
6864                        ui.helper.data('drop-location', -1);    //left
6865                    }
6866
6867                    $this.dragIndicatorTop.offset({
6868                        'left': arrowX,
6869                        'top': topArrowY - 3
6870                    }).show();
6871
6872                    $this.dragIndicatorBottom.offset({
6873                        'left': arrowX,
6874                        'top': bottomArrowY - 3
6875                    }).show();
6876                }
6877            },
6878            stop: function(event, ui) {
6879                //hide dnd arrows
6880                $this.dragIndicatorTop.css({
6881                    'left':0,
6882                    'top':0
6883                }).hide();
6884
6885                $this.dragIndicatorBottom.css({
6886                    'left':0,
6887                    'top':0
6888                }).hide();
6889            },
6890            helper: function() {
6891                var header = $(this),
6892                helper = $('<div class="ui-widget ui-state-default" style="padding:4px 10px;text-align:center;"></div>');
6893
6894                helper.width(header.width());
6895                helper.height(header.height());
6896
6897                helper.html(header.html());
6898
6899                return helper.get(0);
6900            }
6901        }).droppable({
6902            hoverClass:'ui-state-highlight',
6903            tolerance:'pointer',
6904            scope: this.id,
6905            over: function(event, ui) {
6906                ui.helper.data('droppable-column', $(this));
6907            },
6908            drop: function(event, ui) {
6909                var draggedColumnHeader = ui.draggable,
6910                dropLocation = ui.helper.data('drop-location'),
6911                droppedColumnHeader =  $(this),
6912                draggedColumnFooter = null,
6913                droppedColumnFooter = null;
6914
6915                var draggedCells = $this.tbody.find('> tr:not(.ui-expanded-row-content) > td:nth-child(' + (draggedColumnHeader.index() + 1) + ')'),
6916                droppedCells = $this.tbody.find('> tr:not(.ui-expanded-row-content) > td:nth-child(' + (droppedColumnHeader.index() + 1) + ')');
6917
6918                if($this.tfoot.length) {
6919                    var footerColumns = $this.tfoot.find('> tr > td'),
6920                    draggedColumnFooter = footerColumns.eq(draggedColumnHeader.index()),
6921                    droppedColumnFooter = footerColumns.eq(droppedColumnHeader.index());
6922                }
6923
6924                //drop right
6925                if(dropLocation > 0) {
6926                    if($this.cfg.resizableColumns) {
6927                        if(droppedColumnHeader.next().length) {
6928                            droppedColumnHeader.children('span.ui-column-resizer').show();
6929                            draggedColumnHeader.children('span.ui-column-resizer').hide();
6930                        }
6931                    }
6932
6933                    draggedColumnHeader.insertAfter(droppedColumnHeader);
6934
6935                    draggedCells.each(function(i, item) {
6936                        $(this).insertAfter(droppedCells.eq(i));
6937                    });
6938
6939                    if(draggedColumnFooter && droppedColumnFooter) {
6940                        draggedColumnFooter.insertAfter(droppedColumnFooter);
6941                    }
6942
6943                    //sync clone
6944                    if($this.cfg.scrollable) {
6945                        var draggedColumnClone = $(document.getElementById(draggedColumnHeader.attr('id') + '_clone')),
6946                        droppedColumnClone = $(document.getElementById(droppedColumnHeader.attr('id') + '_clone'));
6947                        draggedColumnClone.insertAfter(droppedColumnClone);
6948                    }
6949                }
6950                //drop left
6951                else {
6952                    draggedColumnHeader.insertBefore(droppedColumnHeader);
6953
6954                    draggedCells.each(function(i, item) {
6955                        $(this).insertBefore(droppedCells.eq(i));
6956                    });
6957
6958                    if(draggedColumnFooter && droppedColumnFooter) {
6959                        draggedColumnFooter.insertBefore(droppedColumnFooter);
6960                    }
6961
6962                    //sync clone
6963                    if($this.cfg.scrollable) {
6964                        var draggedColumnClone = $(document.getElementById(draggedColumnHeader.attr('id') + '_clone')),
6965                        droppedColumnClone = $(document.getElementById(droppedColumnHeader.attr('id') + '_clone'));
6966                        draggedColumnClone.insertBefore(droppedColumnClone);
6967                    }
6968                }
6969
6970                //save order
6971                $this.saveColumnOrder();
6972
6973                //fire colReorder event
6974                if($this.hasBehavior('colReorder')) {
6975                    var ext = null;
6976
6977                    if($this.cfg.multiViewState) {
6978                        ext = {
6979                            params: [{name: this.id + '_encodeFeature', value: true}]
6980                        };
6981                    }
6982
6983                    $this.callBehavior('colReorder', ext);
6984                }
6985            }
6986        });
6987    },
6988
6989    saveColumnOrder: function() {
6990        var columnIds = [],
6991        columns = $(this.jqId + ' thead:first th');
6992
6993        columns.each(function(i, item) {
6994            columnIds.push($(item).attr('id'));
6995        });
6996
6997        this.orderStateHolder.val(columnIds.join(','));
6998    },
6999
7000    makeRowsDraggable: function() {
7001        var $this = this,
7002        draggableHandle = this.cfg.rowDragSelector||'td,span:not(.ui-c)';
7003
7004        this.tbody.sortable({
7005            placeholder: 'ui-datatable-rowordering ui-state-active',
7006            cursor: 'move',
7007            handle: draggableHandle,
7008            appendTo: document.body,
7009            start: function(event, ui) {
7010                ui.helper.css('z-index', ++PrimeFaces.zindex);
7011            },
7012            helper: function(event, ui) {
7013                var cells = ui.children(),
7014                helper = $('<div class="ui-datatable ui-widget"><table><tbody class="ui-datatable-data"></tbody></table></div>'),
7015                helperRow = ui.clone(),
7016                helperCells = helperRow.children();
7017
7018                for(var i = 0; i < helperCells.length; i++) {
7019                    helperCells.eq(i).width(cells.eq(i).width());
7020                }
7021
7022                helperRow.appendTo(helper.find('tbody'));
7023
7024                return helper;
7025            },
7026            update: function(event, ui) {
7027                var fromIndex = ui.item.data('ri'),
7028                toIndex = $this.paginator ? $this.paginator.getFirst() + ui.item.index(): ui.item.index();
7029
7030                $this.syncRowParity();
7031
7032                var options = {
7033                    source: $this.id,
7034                    process: $this.id,
7035                    params: [
7036                        {name: $this.id + '_rowreorder', value: true},
7037                        {name: $this.id + '_fromIndex', value: fromIndex},
7038                        {name: $this.id + '_toIndex', value: toIndex},
7039                        {name: this.id + '_skipChildren', value: true}
7040                    ]
7041                }
7042
7043                if($this.hasBehavior('rowReorder')) {
7044                    $this.callBehavior('rowReorder', options);
7045                }
7046                else {
7047                    PrimeFaces.ajax.Request.handle(options);
7048                }
7049            },
7050            change: function(event, ui) {
7051                if($this.cfg.scrollable) {
7052                    PrimeFaces.scrollInView($this.scrollBody, ui.placeholder);
7053                }
7054            }
7055        });
7056    },
7057
7058    syncRowParity: function() {
7059        var rows = this.tbody.children('tr.ui-widget-content'),
7060        first = this.paginator ? this.paginator.getFirst(): 0;
7061
7062        for(var i = first; i < rows.length; i++) {
7063            var row = rows.eq(i);
7064
7065            row.data('ri', i).removeClass('ui-datatable-even ui-datatable-odd');
7066
7067            if(i % 2 === 0)
7068                row.addClass('ui-datatable-even');
7069            else
7070                row.addClass('ui-datatable-odd');
7071
7072        }
7073    },
7074
7075    /**
7076     * Returns if there is any data displayed
7077     */
7078    isEmpty: function() {
7079        return this.tbody.children('tr.ui-datatable-empty-message').length === 1;
7080    },
7081
7082    getSelectedRowsCount: function() {
7083        return this.isSelectionEnabled() ? this.selection.length : 0;
7084    },
7085
7086    updateHeaderCheckbox: function() {
7087        if(this.isEmpty()) {
7088            this.uncheckHeaderCheckbox();
7089            this.disableHeaderCheckbox();
7090        }
7091        else if(!this.cfg.selectionPageOnly) {
7092            if(this.selection.includes('@all')) {
7093                this.enableHeaderCheckbox();
7094                this.checkHeaderCheckbox();
7095            }
7096        }
7097        else {
7098            var checkboxes, selectedCheckboxes, enabledCheckboxes, disabledCheckboxes;
7099
7100            if(this.cfg.nativeElements) {
7101                checkboxes = this.tbody.find('> tr > td.ui-selection-column > :checkbox');
7102                enabledCheckboxes = checkboxes.filter(':enabled');
7103                disabledCheckboxes = checkboxes.filter(':disabled');
7104                selectedCheckboxes = enabledCheckboxes.filter(':checked');
7105            }
7106            else {
7107                checkboxes = this.tbody.find('> tr > td.ui-selection-column .ui-chkbox-box');
7108                enabledCheckboxes = checkboxes.filter(':not(.ui-state-disabled)');
7109                disabledCheckboxes = checkboxes.filter('.ui-state-disabled');
7110                selectedCheckboxes = enabledCheckboxes.prev().children(':checked');
7111            }
7112
7113            if(enabledCheckboxes.length && enabledCheckboxes.length === selectedCheckboxes.length)
7114               this.checkHeaderCheckbox();
7115            else
7116               this.uncheckHeaderCheckbox();
7117
7118            if(checkboxes.length === disabledCheckboxes.length)
7119               this.disableHeaderCheckbox();
7120            else
7121               this.enableHeaderCheckbox();
7122        }
7123    },
7124
7125    checkHeaderCheckbox: function() {
7126        if(this.cfg.nativeElements) {
7127            this.checkAllToggler.prop('checked', true);
7128        }
7129        else {
7130            this.checkAllToggler.addClass('ui-state-active').children('span.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
7131            this.checkAllTogglerInput.prop('checked', true).attr('aria-checked', true);
7132        }
7133    },
7134
7135    uncheckHeaderCheckbox: function() {
7136        if(this.cfg.nativeElements) {
7137            this.checkAllToggler.prop('checked', false);
7138        }
7139        else {
7140            this.checkAllToggler.removeClass('ui-state-active').children('span.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
7141            this.checkAllTogglerInput.prop('checked', false).attr('aria-checked', false);
7142        }
7143    },
7144
7145    disableHeaderCheckbox: function() {
7146        if(this.cfg.nativeElements)
7147            this.checkAllToggler.prop('disabled', true);
7148        else
7149            this.checkAllToggler.addClass('ui-state-disabled');
7150    },
7151
7152    enableHeaderCheckbox: function() {
7153        if(this.cfg.nativeElements)
7154            this.checkAllToggler.prop('disabled', false);
7155        else
7156            this.checkAllToggler.removeClass('ui-state-disabled');
7157    },
7158
7159    setupStickyHeader: function() {
7160        var table = this.thead.parent(),
7161        offset = table.offset(),
7162        win = $(window),
7163        $this = this,
7164        orginTableContent = this.jq.find('> .ui-datatable-tablewrapper > table'),
7165        fixedElementsOnTop = this.cfg.stickyTopAt ? $(this.cfg.stickyTopAt) : null,
7166        fixedElementsHeight = 0;
7167
7168        if (fixedElementsOnTop && fixedElementsOnTop.length) {
7169            for (var i = 0; i < fixedElementsOnTop.length; i++) {
7170                fixedElementsHeight += fixedElementsOnTop.eq(i).outerHeight();
7171            }
7172        }
7173
7174        this.stickyContainer = $('<div class="ui-datatable ui-datatable-sticky ui-widget"><table></table></div>');
7175        this.clone = this.thead.clone(false);
7176        this.stickyContainer.children('table').append(this.thead);
7177        table.prepend(this.clone);
7178
7179        this.stickyContainer.css({
7180            position: 'absolute',
7181            width: table.outerWidth(),
7182            top: offset.top,
7183            left: offset.left,
7184            'z-index': ++PrimeFaces.zindex
7185        });
7186
7187        this.jq.prepend(this.stickyContainer);
7188
7189        if(this.cfg.resizableColumns) {
7190            this.relativeHeight = 0;
7191        }
7192
7193        PrimeFaces.utils.registerScrollHandler(this, 'scroll.' + this.id, function() {
7194            var scrollTop = win.scrollTop(),
7195            tableOffset = table.offset();
7196
7197            if(scrollTop + fixedElementsHeight > tableOffset.top) {
7198                $this.stickyContainer.css({
7199                                        'position': 'fixed',
7200                                        'top': fixedElementsHeight
7201                                    })
7202                                    .addClass('ui-shadow ui-sticky');
7203
7204                if($this.cfg.resizableColumns) {
7205                    $this.relativeHeight = (scrollTop + fixedElementsHeight) - tableOffset.top;
7206                }
7207
7208                if(scrollTop + fixedElementsHeight >= (tableOffset.top + $this.tbody.height()))
7209                    $this.stickyContainer.hide();
7210                else
7211                    $this.stickyContainer.show();
7212            }
7213            else {
7214                $this.stickyContainer.css({
7215                                        'position': 'absolute',
7216                                        'top': tableOffset.top
7217                                    })
7218                                    .removeClass('ui-shadow ui-sticky');
7219
7220                if($this.stickyContainer.is(':hidden')) {
7221                    $this.stickyContainer.show();
7222                }
7223
7224                if($this.cfg.resizableColumns) {
7225                    $this.relativeHeight = 0;
7226                }
7227            }
7228        });
7229
7230        PrimeFaces.utils.registerResizeHandler(this, 'resize.sticky-' + this.id, null, function(e) {
7231            var _delay = e.data.delay;
7232
7233            if (_delay !== null && typeof _delay === 'number' && _delay > -1) {
7234                if ($this.resizeTimeout) {
7235                    clearTimeout($this.resizeTimeout);
7236                }
7237
7238                $this.stickyContainer.hide();
7239                $this.resizeTimeout = setTimeout(function() {
7240                    $this.stickyContainer.css('left', orginTableContent.offset().left);
7241                    $this.stickyContainer.width(table.outerWidth());
7242                    $this.stickyContainer.show();
7243                }, _delay);
7244            }
7245            else {
7246                $this.stickyContainer.width(table.outerWidth());
7247            }
7248        }, { delay: null });
7249
7250        //filter support
7251        this.clone.find('.ui-column-filter').prop('disabled', true);
7252    },
7253
7254    getFocusableTbody: function() {
7255        return this.tbody;
7256    },
7257
7258    reclone: function() {
7259        this.clone.remove();
7260        this.clone = this.thead.clone(false);
7261        this.jq.find('.ui-datatable-tablewrapper > table').prepend(this.clone);
7262    },
7263
7264    addRow: function() {
7265        var $this = this,
7266        options = {
7267            source: this.id,
7268            process: this.id,
7269            update: this.id,
7270            params: [{name: this.id + '_addrow', value: true},
7271                    {name: this.id + '_skipChildren', value: true},
7272                    {name: this.id + '_encodeFeature', value: true}],
7273            onsuccess: function(responseXML, status, xhr) {
7274                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
7275                    widget: $this,
7276                    handle: function(content) {
7277                        this.tbody.append(content);
7278                    }
7279                });
7280
7281                return true;
7282            }
7283        };
7284
7285        PrimeFaces.ajax.Request.handle(options);
7286    },
7287
7288    clearCacheMap: function() {
7289        this.cacheMap = {};
7290    },
7291
7292    loadDataWithCache: function(newState) {
7293        var isRppChanged = false;
7294        if(this.cacheRows != newState.rows) {
7295            this.clearCacheMap();
7296            this.cacheRows = newState.rows;
7297            isRppChanged = true;
7298        }
7299
7300        var pageFirst = newState.first,
7301            nextPageFirst = newState.rows + pageFirst,
7302            lastPageFirst = this.cfg.paginator.pageCount * newState.rows,
7303            hasNextPage = (!this.cacheMap[nextPageFirst]) && nextPageFirst < lastPageFirst;
7304
7305        if(this.cacheMap[pageFirst] && !isRppChanged) {
7306            this.updateData(this.cacheMap[pageFirst]);
7307            this.paginator.cfg.page = newState.page;
7308            this.paginator.updateUI();
7309
7310            if(!hasNextPage) {
7311                this.updatePageState(newState);
7312            }
7313        }
7314        else {
7315            this.paginate(newState);
7316        }
7317
7318        if(hasNextPage) {
7319            this.fetchNextPage(newState);
7320        }
7321    },
7322
7323    updateReflowDD: function(columnHeader, sortOrder) {
7324        if(this.reflowDD && this.cfg.reflow) {
7325            var options = this.reflowDD.children('option'),
7326            orderIndex = sortOrder > 0 ? 0 : 1;
7327
7328            options.filter(':selected').prop('selected', false);
7329            options.filter('[value="' + $.escapeSelector(columnHeader.index() + '_' + orderIndex) + '"]').prop('selected', true);
7330        }
7331    },
7332
7333    groupRows: function() {
7334        var rows = this.tbody.children('tr');
7335        for(var i = 0; i < this.cfg.groupColumnIndexes.length; i++) {
7336            this.groupRow(this.cfg.groupColumnIndexes[i], rows);
7337        }
7338
7339        rows.children('td.ui-duplicated-column').remove();
7340    },
7341
7342    groupRow: function(colIndex, rows) {
7343        var groupStartIndex = null, rowGroupCellData = null, rowGroupCount = null;
7344
7345        for(var i = 0; i < rows.length; i++) {
7346            var row = rows.eq(i);
7347            var column = row.children('td').eq(colIndex);
7348            var columnData = column.text();
7349            if(rowGroupCellData != columnData) {
7350                groupStartIndex = i;
7351                rowGroupCellData = columnData;
7352                rowGroupCount = 1;
7353
7354                if (this.cfg.liveScroll && column[0].hasAttribute('rowspan')) {
7355                    rowGroupCount = parseInt(column.attr('rowspan'));
7356                    i += rowGroupCount - 1;
7357                }
7358                
7359                row.addClass('ui-datatable-grouped-row');
7360            }
7361            else {
7362                column.addClass('ui-duplicated-column');
7363                rowGroupCount++;
7364            }
7365
7366            if(groupStartIndex != null && rowGroupCount > 1) {
7367                rows.eq(groupStartIndex).children('td').eq(colIndex).attr('rowspan', rowGroupCount);
7368            }
7369        }
7370    },
7371
7372    bindToggleRowGroupEvents: function() {
7373        var expandableRows = this.tbody.children('tr.ui-rowgroup-header'),
7374            toggler = expandableRows.find('> td:first > a.ui-rowgroup-toggler');
7375
7376        toggler.off('click.dataTable-rowgrouptoggler').on('click.dataTable-rowgrouptoggler', function(e) {
7377           var link = $(this),
7378           togglerIcon = link.children('.ui-rowgroup-toggler-icon'),
7379           parentRow = link.closest('tr.ui-rowgroup-header');
7380
7381           if(togglerIcon.hasClass('ui-icon-circle-triangle-s')) {
7382               link.attr('aria-expanded', false);
7383               togglerIcon.addClass('ui-icon-circle-triangle-e').removeClass('ui-icon-circle-triangle-s');
7384               parentRow.nextUntil('tr.ui-rowgroup-header').hide();
7385           }
7386           else {
7387               link.attr('aria-expanded', true);
7388               togglerIcon.addClass('ui-icon-circle-triangle-s').removeClass('ui-icon-circle-triangle-e');
7389               parentRow.nextUntil('tr.ui-rowgroup-header').show();
7390           }
7391
7392           e.preventDefault();
7393        });
7394    },
7395
7396    calculateColspan: function() {
7397        var visibleHeaderColumns = this.thead.find('> tr:first th:not(.ui-helper-hidden):not(.ui-grouped-column)'),
7398            colSpanValue = 0;
7399
7400        for(var i = 0; i < visibleHeaderColumns.length; i++) {
7401            var column = visibleHeaderColumns.eq(i);
7402            if(column.is('[colspan]')) {
7403                colSpanValue += parseInt(column.attr('colspan'));
7404            }
7405            else {
7406                colSpanValue++;
7407            }
7408        }
7409
7410        return colSpanValue;
7411    },
7412
7413    updateColspan: function(row, colspanValue) {
7414        row.children('td').attr('colspan', colspanValue || this.calculateColspan());
7415    },
7416    
7417    updateEmptyColspan: function() {
7418        var emptyRow = this.tbody.children('tr:first');
7419        if(emptyRow && emptyRow.hasClass('ui-datatable-empty-message')) {
7420            this.updateColspan(emptyRow);
7421        }
7422    },
7423    
7424    updateRowspan: function(row) {
7425        if (this.cfg.groupColumnIndexes) {
7426            var isGroupedRow = row.hasClass('ui-datatable-grouped-row');
7427            var groupedRow = isGroupedRow ? row : row.prevAll('.ui-datatable-grouped-row:first');
7428            var groupedColumn = groupedRow.children('.ui-grouped-column:first');
7429            var rowSpan = groupedRow.nextUntil('.ui-datatable-grouped-row').not(':hidden').length + 1;
7430            var diff = rowSpan - parseInt(groupedColumn.attr('rowspan') || 1);
7431            groupedColumn.attr('rowspan', rowSpan);
7432    
7433            var groupedColumnIndex = groupedColumn.index();
7434            if (groupedColumnIndex > 0) {
7435                var columns = row.children('td:visible');
7436                for (var i = 0; i < groupedColumnIndex; i++) {
7437                    var column = columns.eq(i);
7438                    if (column) {
7439                        column.attr('rowspan', parseInt(column.attr('rowspan') || 1) + diff);
7440                    }
7441                }
7442            }
7443        }
7444    },
7445
7446    updateExpandedRowsColspan: function() {
7447        var colspanValue = this.calculateColspan(),
7448            $this = this;
7449        this.getExpandedRows().each(function() {
7450            $this.updateColspan($(this).next('.ui-expanded-row-content'), colspanValue);
7451        });
7452    },
7453
7454    updateResizableState: function(columnHeader, nextColumnHeader, table, newWidth, nextColumnWidth) {
7455        if(this.cfg.multiViewState) {
7456            var expandMode = (this.cfg.resizeMode === 'expand'),
7457            currentColumnId = columnHeader.attr('id'),
7458            nextColumnId = nextColumnHeader.attr('id'),
7459            tableId = this.id + "_tableWidthState",
7460            currentColumnState = currentColumnId + '_' + newWidth,
7461            nextColumnState = nextColumnId + '_' + nextColumnWidth,
7462            tableState = tableId + '_' + parseInt(table.css('width')),
7463            currentColumnMatch = false,
7464            nextColumnMatch = false,
7465            tableMatch = false;
7466
7467            for(var i = 0; i < this.resizableState.length; i++) {
7468                var state = this.resizableState[i];
7469                if(state.indexOf(currentColumnId) === 0) {
7470                    this.resizableState[i] = currentColumnState;
7471                    currentColumnMatch = true;
7472                }
7473                else if(!expandMode && state.indexOf(nextColumnId) === 0) {
7474                    this.resizableState[i] = nextColumnState;
7475                    nextColumnMatch = true;
7476                }
7477                else if(expandMode && state.indexOf(tableId) === 0) {
7478                    this.resizableState[i] = tableState;
7479                    tableMatch = true;
7480                }
7481            }
7482
7483            if(!currentColumnMatch) {
7484                this.resizableState.push(currentColumnState);
7485            }
7486
7487            if(!expandMode && !nextColumnMatch) {
7488                this.resizableState.push(nextColumnState);
7489            }
7490
7491            if(expandMode && !tableMatch) {
7492                this.resizableState.push(tableState);
7493            }
7494
7495            this.resizableStateHolder.val(this.resizableState.join(','));
7496        }
7497    },
7498
7499    findColWidthInResizableState: function(id) {
7500        for(var i = 0; i < this.resizableState.length; i++) {
7501            var state = this.resizableState[i];
7502            if(state.indexOf(id) === 0) {
7503                return state.substring(state.lastIndexOf('_') + 1, state.length);
7504            }
7505        }
7506    },
7507
7508    updateColumnsView: function() {
7509        if(this.isEmpty()) {
7510            return;
7511        }
7512
7513        // update the visibility of columns but ignore expanded rows
7514        for(var i = 0; i < this.headers.length; i++) {
7515            var header = this.headers.eq(i),
7516                col = this.tbody.find('> tr:not(.ui-expanded-row-content) > td:nth-child(' + (header.index() + 1) + ')');
7517
7518            if(header.hasClass('ui-helper-hidden')) {
7519                col.addClass('ui-helper-hidden');
7520            }
7521            else {
7522                col.removeClass('ui-helper-hidden');
7523            }
7524        }
7525
7526        // update the colspan of the expanded rows
7527        if(this.cfg.expansion) {
7528            this.updateExpandedRowsColspan();
7529        }
7530    },
7531
7532    resetVirtualScrollBody: function() {
7533        this.bodyTable.css('top', '0px');
7534        this.scrollBody.scrollTop(0);
7535        this.clearScrollState();
7536    }
7537
7538});
7539
7540/**
7541 * PrimeFaces DataTable with Frozen Columns Widget
7542 */
7543PrimeFaces.widget.FrozenDataTable = PrimeFaces.widget.DataTable.extend({
7544
7545    setupScrolling: function() {
7546        this.scrollLayout = this.jq.find('> table > tbody > tr > td.ui-datatable-frozenlayout-right');
7547        this.frozenLayout = this.jq.find('> table > tbody > tr > td.ui-datatable-frozenlayout-left');
7548        this.scrollContainer = this.jq.find('> table > tbody > tr > td.ui-datatable-frozenlayout-right > .ui-datatable-scrollable-container');
7549        this.frozenContainer = this.jq.find('> table > tbody > tr > td.ui-datatable-frozenlayout-left > .ui-datatable-frozen-container');
7550        this.scrollHeader = this.scrollContainer.children('.ui-datatable-scrollable-header');
7551        this.scrollHeaderBox = this.scrollHeader.children('div.ui-datatable-scrollable-header-box');
7552        this.scrollBody = this.scrollContainer.children('.ui-datatable-scrollable-body');
7553        this.scrollFooter = this.scrollContainer.children('.ui-datatable-scrollable-footer');
7554        this.scrollFooterBox = this.scrollFooter.children('div.ui-datatable-scrollable-footer-box');
7555        this.scrollStateHolder = $(this.jqId + '_scrollState');
7556        this.scrollHeaderTable = this.scrollHeaderBox.children('table');
7557        this.scrollBodyTable = this.cfg.virtualScroll ? this.scrollBody.children('div').children('table') : this.scrollBody.children('table');
7558        this.scrollThead = this.thead.eq(1);
7559        this.scrollTbody = this.tbody.eq(1);
7560        this.scrollFooterTable = this.scrollFooterBox.children('table');
7561        this.scrollFooterCols = this.scrollFooter.find('> .ui-datatable-scrollable-footer-box > table > tfoot > tr > td');
7562        this.frozenHeader = this.frozenContainer.children('.ui-datatable-scrollable-header');
7563        this.frozenBody = this.frozenContainer.children('.ui-datatable-scrollable-body');
7564        this.frozenBodyTable = this.cfg.virtualScroll ? this.frozenBody.children('div').children('table') : this.frozenBody.children('table');
7565        this.frozenThead = this.thead.eq(0);
7566        this.frozenTbody = this.tbody.eq(0);
7567        this.frozenFooter = this.frozenContainer.children('.ui-datatable-scrollable-footer');
7568        this.frozenFooterTable = this.frozenFooter.find('> .ui-datatable-scrollable-footer-box > table');
7569        this.frozenFooterCols = this.frozenFooter.find('> .ui-datatable-scrollable-footer-box > table > tfoot > tr > td');
7570        this.percentageScrollHeight = this.cfg.scrollHeight && (this.cfg.scrollHeight.indexOf('%') !== -1);
7571        this.percentageScrollWidth = this.cfg.scrollWidth && (this.cfg.scrollWidth.indexOf('%') !== -1);
7572
7573        this.frozenThead.find('> tr > th').addClass('ui-frozen-column');
7574
7575        var $this = this,
7576        scrollBarWidth = this.getScrollbarWidth() + 'px',
7577        hScrollWidth = this.scrollBody[0].scrollWidth;
7578
7579        if(this.cfg.scrollHeight) {
7580            if(this.percentageScrollHeight) {
7581                this.adjustScrollHeight();
7582            }
7583
7584            if(this.hasVerticalOverflow()) {
7585                this.scrollHeaderBox.css('margin-right', scrollBarWidth);
7586                this.scrollFooterBox.css('margin-right', scrollBarWidth);
7587            }
7588        }
7589
7590        if(this.cfg.selectionMode) {
7591            this.scrollTbody.removeAttr('tabindex');
7592        }
7593
7594        this.fixColumnWidths();
7595
7596        if(this.cfg.scrollWidth) {
7597            if(this.percentageScrollWidth)
7598                this.adjustScrollWidth();
7599            else
7600                this.setScrollWidth(parseInt(this.cfg.scrollWidth));
7601
7602            if(this.hasVerticalOverflow()) {
7603                var browser = PrimeFaces.env.browser;
7604                if(browser.webkit === true || browser.mozilla === true)
7605                    this.frozenBody.append('<div style="height:' + scrollBarWidth + ';border:1px solid transparent"></div>');
7606                else
7607                    this.frozenBodyTable.css('margin-bottom', scrollBarWidth);
7608            }
7609        }
7610
7611        this.cloneHead();
7612
7613        this.restoreScrollState();
7614
7615        if(this.cfg.liveScroll) {
7616            this.scrollOffset = 0;
7617            this.cfg.liveScrollBuffer = (100 - this.cfg.liveScrollBuffer) / 100;
7618            this.shouldLiveScroll = true;
7619            this.loadingLiveScroll = false;
7620            this.allLoadedLiveScroll = $this.cfg.scrollStep >= $this.cfg.scrollLimit;
7621        }
7622
7623        if(this.cfg.virtualScroll) {
7624            var row = this.scrollTbody.children('tr.ui-widget-content');
7625            if(row) {
7626                this.rowHeight = row.outerHeight();
7627                this.scrollBody.children('div').css('height', parseFloat((this.cfg.scrollLimit * this.rowHeight) + 'px'));
7628                this.frozenBody.children('div').css('height', parseFloat((this.cfg.scrollLimit * this.rowHeight) + 'px'));
7629            }
7630
7631            if(!this.cfg.scrollHeight) {
7632                this.frozenBody.css('height', this.scrollBody.height());
7633            }
7634        }
7635
7636        this.scrollBody.scroll(function() {
7637            var scrollLeft = $this.scrollBody.scrollLeft(),
7638            scrollTop = $this.scrollBody.scrollTop();
7639            
7640            if ($this.isRTL) {
7641                $this.scrollHeaderBox.css('margin-right', (scrollLeft - hScrollWidth + this.clientWidth));
7642                $this.scrollFooterBox.css('margin-right', (scrollLeft - hScrollWidth + this.clientWidth));
7643            }
7644            else {
7645                $this.scrollHeaderBox.css('margin-left', -scrollLeft);
7646                $this.scrollFooterBox.css('margin-left', -scrollLeft);
7647            }
7648            
7649            $this.frozenBody.scrollTop(scrollTop);
7650
7651            if($this.cfg.virtualScroll) {
7652                var virtualScrollBody = this;
7653
7654                clearTimeout($this.scrollTimeout);
7655                $this.scrollTimeout = setTimeout(function() {
7656                    var viewportHeight = $this.scrollBody.outerHeight(),
7657                    tableHeight = $this.scrollBodyTable.outerHeight(),
7658                    pageHeight = $this.rowHeight * $this.cfg.scrollStep,
7659                    virtualTableHeight = parseFloat(($this.cfg.scrollLimit * $this.rowHeight) + 'px'),
7660                    pageCount = (virtualTableHeight / pageHeight)||1;
7661
7662                    if(virtualScrollBody.scrollTop + viewportHeight > parseFloat($this.scrollBodyTable.css('top')) + tableHeight || virtualScrollBody.scrollTop < parseFloat($this.scrollBodyTable.css('top'))) {
7663                        var page = Math.floor((virtualScrollBody.scrollTop * pageCount) / (virtualScrollBody.scrollHeight)) + 1;
7664                        $this.loadRowsWithVirtualScroll(page, function () {
7665                            $this.scrollBodyTable.css('top', ((page - 1) * pageHeight) + 'px');
7666                            $this.frozenBodyTable.css('top', ((page - 1) * pageHeight) + 'px');
7667                        });
7668                    }
7669                }, 200);
7670            }
7671            else if($this.shouldLiveScroll) {
7672                var scrollTop = Math.ceil(this.scrollTop),
7673                scrollHeight = this.scrollHeight,
7674                viewportHeight = this.clientHeight;
7675
7676                if((scrollTop >= ((scrollHeight * $this.cfg.liveScrollBuffer) - (viewportHeight))) && $this.shouldLoadLiveScroll()) {
7677                    $this.loadLiveRows();
7678                }
7679            }
7680
7681            $this.saveScrollState();
7682        });
7683
7684        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.jq, function() {
7685            if ($this.percentageScrollHeight) {
7686                $this.adjustScrollHeight();
7687            }
7688            if ($this.percentageScrollWidth) {
7689                $this.adjustScrollWidth();
7690            }
7691        });
7692    },
7693
7694    cloneHead: function() {
7695        if (this.frozenTheadClone) {
7696            this.frozenTheadClone.remove();
7697        }
7698        this.frozenTheadClone = this.cloneTableHeader(this.frozenThead, this.frozenBodyTable);
7699
7700        if (this.scrollTheadClone) {
7701            this.scrollTheadClone.remove();
7702        }
7703        this.scrollTheadClone = this.cloneTableHeader(this.scrollThead, this.scrollBodyTable);
7704    },
7705
7706    hasVerticalOverflow: function() {
7707        return this.scrollBodyTable.outerHeight() > this.scrollBody.outerHeight();
7708    },
7709
7710    adjustScrollHeight: function() {
7711        var relativeHeight = this.jq.parent().innerHeight() * (parseInt(this.cfg.scrollHeight) / 100),
7712        headerChilden = this.jq.children('.ui-datatable-header'),
7713        footerChilden = this.jq.children('.ui-datatable-footer'),
7714        tableHeaderHeight = (headerChilden.length > 0) ? headerChilden.outerHeight(true) : 0,
7715        tableFooterHeight = (footerChilden.length > 0) ? footerChilden.outerHeight(true) : 0,
7716        scrollersHeight = (this.scrollHeader.innerHeight() + this.scrollFooter.innerHeight()),
7717        paginatorsHeight = this.paginator ? this.paginator.getContainerHeight(true) : 0,
7718        height = (relativeHeight - (scrollersHeight + paginatorsHeight + tableHeaderHeight + tableFooterHeight));
7719
7720        if(this.cfg.virtualScroll) {
7721            this.scrollBody.css('max-height', height);
7722            this.frozenBody.css('max-height', height);
7723        }
7724        else {
7725            this.scrollBody.height(height);
7726            this.frozenBody.height(height);
7727        }
7728    },
7729
7730    //@Override
7731    adjustScrollWidth: function() {
7732        var scrollLayoutWidth = this.jq.parent().innerWidth() - this.frozenLayout.innerWidth(),
7733        width = parseInt((scrollLayoutWidth * (parseInt(this.cfg.scrollWidth) / 100)));
7734
7735        this.setScrollWidth(width);
7736    },
7737
7738    setScrollWidth: function(width) {
7739        this.scrollHeader.width(width);
7740        this.scrollBody.css('margin-right', 0).width(width);
7741        this.scrollFooter.width(width);
7742
7743        var $this = this,
7744        headerWidth = width + this.frozenLayout.width();
7745
7746        this.jq.children('.ui-widget-header').each(function() {
7747            $this.setOuterWidth($(this), headerWidth);
7748        });
7749    },
7750
7751    fixColumnWidths: function() {
7752        if(!this.columnWidthsFixed) {
7753
7754            if(this.cfg.scrollable) {
7755                this._fixColumnWidths(this.scrollHeader, this.scrollFooterCols, this.scrollColgroup);
7756                this._fixColumnWidths(this.frozenHeader, this.frozenFooterCols, this.frozenColgroup);
7757            }
7758            else {
7759                this.jq.find('> .ui-datatable-tablewrapper > table > thead > tr > th').each(function() {
7760                    var col = $(this),
7761                    colStyle = col[0].style,
7762                    width = colStyle.width||col.width();
7763                    col.width(width);
7764                });
7765            }
7766
7767            this.columnWidthsFixed = true;
7768        }
7769    },
7770
7771    _fixColumnWidths: function(header, footerCols) {
7772        var $this = this;
7773
7774        header.find('> .ui-datatable-scrollable-header-box > table > thead > tr > th').each(function() {
7775            var headerCol = $(this),
7776            colIndex = headerCol.index(),
7777            headerStyle = headerCol[0].style,
7778            width = headerStyle.width||headerCol.width();
7779
7780            $this.setOuterWidth(headerCol, width);
7781
7782            if(footerCols.length > 0) {
7783                var footerCol = footerCols.eq(colIndex);
7784                $this.setOuterWidth(footerCol, width);
7785            }
7786        });
7787    },
7788
7789    //@Override
7790    updateData: function(data, clear) {
7791        var table = $('<table><tbody>' + data + '</tbody></table>'),
7792        rows = table.find('> tbody > tr'),
7793        empty = (clear === undefined) ? true: clear;
7794
7795        if(empty) {
7796            this.frozenTbody.children().remove();
7797            this.scrollTbody.children().remove();
7798        }
7799
7800        //find slice index by checking how many rendered columns there are in frozen part
7801        var firstRow = this.frozenTbody.children('tr:first'),
7802        frozenColumnCount = firstRow.length ? firstRow.children('td').length: this.cfg.frozenColumns;
7803
7804        for(var i = 0; i < rows.length; i++) {
7805            var row = rows.eq(i),
7806            columns = row.children('td'),
7807            frozenRow = this.copyRow(row),
7808            scrollableRow = this.copyRow(row);
7809
7810            if(row.hasClass('ui-datatable-empty-message')) {
7811                var colspan = columns.attr('colspan'),
7812                cloneColumns = columns.clone();
7813
7814                frozenRow.append(columns.attr('colspan', this.cfg.frozenColumns));
7815                scrollableRow.append(cloneColumns.attr('colspan', (colspan - this.cfg.frozenColumns)));
7816            }
7817            else {
7818                frozenRow.append(columns.slice(0, frozenColumnCount));
7819                scrollableRow.append(columns.slice(frozenColumnCount));
7820            }
7821
7822            this.frozenTbody.append(frozenRow);
7823            this.scrollTbody.append(scrollableRow);
7824        }
7825
7826        this.postUpdateData();
7827    },
7828
7829    copyRow: function(original) {
7830        return $('<tr></tr>').attr('data-ri', original.data('ri')).attr('data-rk', original.data('rk')).addClass(original.attr('class')).
7831                attr('role', 'row').attr('aria-selected', original.attr('aria-selected'));
7832    },
7833
7834    getThead: function() {
7835        return $(this.jqId + '_frozenThead,' + this.jqId + '_scrollableThead');
7836    },
7837
7838    getTbody: function() {
7839        return $(this.jqId + '_frozenTbody,' + this.jqId + '_scrollableTbody');
7840    },
7841
7842    getTfoot: function() {
7843        return $(this.jqId + '_frozenTfoot,' + this.jqId + '_scrollableTfoot');
7844    },
7845
7846    bindRowHover: function(selector) {
7847        var $this = this;
7848
7849        this.tbody.off('mouseover.datatable mouseout.datatable', selector)
7850                    .on('mouseover.datatable', selector, null, function() {
7851                        var row = $(this),
7852                        twinRow = $this.getTwinRow(row);
7853
7854                        if(!row.hasClass('ui-state-highlight')) {
7855                            row.addClass('ui-state-hover');
7856                            twinRow.addClass('ui-state-hover');
7857                        }
7858                    })
7859                    .on('mouseout.datatable', selector, null, function() {
7860                        var row = $(this),
7861                        twinRow = $this.getTwinRow(row);
7862
7863                        if(!row.hasClass('ui-state-highlight')) {
7864                            row.removeClass('ui-state-hover');
7865                            twinRow.removeClass('ui-state-hover');
7866                        }
7867                    });
7868    },
7869
7870    getTwinRow: function(row) {
7871        var twinTbody = (this.tbody.index(row.parent()) === 0) ? this.tbody.eq(1) : this.tbody.eq(0);
7872
7873        return twinTbody.children().eq(row.index());
7874    },
7875
7876    //@Override
7877    highlightRow: function(row) {
7878        this._super(row);
7879        this._super(this.getTwinRow(row));
7880    },
7881
7882    //@Override
7883    unhighlightRow: function(row) {
7884        this._super(row);
7885        this._super(this.getTwinRow(row));
7886    },
7887
7888    //@Override
7889    displayExpandedRow: function(row, content) {
7890        var twinRow = this.getTwinRow(row);
7891        row.after(content);
7892        this.updateRowspan(row);
7893        var expansionRow = row.next();
7894        this.updateColspan(expansionRow);
7895        expansionRow.show();
7896
7897        twinRow.after('<tr class="ui-expanded-row-content ui-widget-content"><td></td></tr>');
7898        twinRow.next().children('td').attr('colspan', this.updateColspan(twinRow)).height(expansionRow.children('td').height());
7899    },
7900
7901    //@Override
7902    collapseRow: function(row) {
7903        this._super(row);
7904        this._super(this.getTwinRow(row));
7905    },
7906
7907    //@Override
7908    getExpandedRows: function() {
7909        return this.frozenTbody.children('.ui-expanded-row');
7910    },
7911
7912    //@Override
7913    showRowEditors: function(row) {
7914        this._super(row);
7915        this._super(this.getTwinRow(row));
7916    },
7917
7918    //@Override
7919    updateRow: function(row, content) {
7920        var table = $('<table><tbody>' + content + '</tbody></table>'),
7921        newRow = table.find('> tbody > tr'),
7922        columns = newRow.children('td'),
7923        frozenRow = this.copyRow(newRow),
7924        scrollableRow = this.copyRow(newRow),
7925        twinRow = this.getTwinRow(row);
7926
7927        frozenRow.append(columns.slice(0, this.cfg.frozenColumns));
7928        scrollableRow.append(columns.slice(this.cfg.frozenColumns));
7929
7930        row.replaceWith(frozenRow);
7931        twinRow.replaceWith(scrollableRow);
7932    },
7933
7934    //@Override
7935    invalidateRow: function(index) {
7936        this.frozenTbody.children('tr').eq(index).addClass('ui-widget-content ui-row-editing ui-state-error');
7937        this.scrollTbody.children('tr').eq(index).addClass('ui-widget-content ui-row-editing ui-state-error');
7938    },
7939
7940    //@Override
7941    getRowEditors: function(row) {
7942        return row.find('div.ui-cell-editor').add(this.getTwinRow(row).find('div.ui-cell-editor'));
7943    },
7944
7945    //@Override
7946    findGroupResizer: function(ui) {
7947        var resizer = this._findGroupResizer(ui, this.frozenGroupResizers);
7948        if(resizer) {
7949            return resizer;
7950        }
7951        else {
7952            return this._findGroupResizer(ui, this.scrollGroupResizers);
7953        }
7954    },
7955
7956    _findGroupResizer: function(ui, resizers) {
7957        for(var i = 0; i < resizers.length; i++) {
7958            var groupResizer = resizers.eq(i);
7959            if(groupResizer.offset().left === ui.helper.data('originalposition').left) {
7960                return groupResizer;
7961            }
7962        }
7963
7964        return null;
7965    },
7966
7967    //@Override
7968    addResizers: function() {
7969        var frozenColumns = this.frozenThead.find('> tr > th.ui-resizable-column'),
7970        scrollableColumns = this.scrollThead.find('> tr > th.ui-resizable-column');
7971
7972        frozenColumns.prepend('<span class="ui-column-resizer">&nbsp;</span>');
7973        scrollableColumns.prepend('<span class="ui-column-resizer">&nbsp;</span>')
7974
7975        if(this.cfg.resizeMode === 'fit') {
7976            frozenColumns.filter(':last-child').addClass('ui-frozen-column-last');
7977            scrollableColumns.filter(':last-child').children('span.ui-column-resizer').hide();
7978        }
7979
7980        if(this.hasColumnGroup) {
7981            this.frozenGroupResizers = this.frozenThead.find('> tr:first > th > .ui-column-resizer');
7982            this.scrollGroupResizers = this.scrollThead.find('> tr:first > th > .ui-column-resizer');
7983        }
7984    },
7985
7986    //@Override
7987    resize: function(event, ui) {
7988        var columnHeader = null,
7989        change = null,
7990        newWidth = null,
7991        nextColumnWidth = null,
7992        expandMode = (this.cfg.resizeMode === 'expand');
7993
7994        if(this.hasColumnGroup) {
7995            var groupResizer = this.findGroupResizer(ui);
7996            if(!groupResizer) {
7997                return;
7998            }
7999
8000            columnHeader = groupResizer.parent();
8001        }
8002        else {
8003            columnHeader = ui.helper.parent();
8004        }
8005
8006        var nextColumnHeader = columnHeader.next();
8007
8008        var colIndex = columnHeader.index(),
8009        lastFrozen = columnHeader.hasClass('ui-frozen-column-last');
8010
8011        if(this.cfg.liveResize) {
8012            change = columnHeader.outerWidth() - (event.pageX - columnHeader.offset().left),
8013            newWidth = (columnHeader.width() - change),
8014            nextColumnWidth = (nextColumnHeader.width() + change);
8015        }
8016        else {
8017            change = (ui.position.left - ui.originalPosition.left),
8018            newWidth = (columnHeader.width() + change),
8019            nextColumnWidth = (nextColumnHeader.width() - change);
8020        }
8021
8022        var minWidth = parseInt(columnHeader.css('min-width'));
8023        minWidth = (minWidth == 0) ? 15 : minWidth;
8024        var shouldChange = (expandMode && newWidth > minWidth) || (lastFrozen ? (newWidth > minWidth) : (newWidth > minWidth && nextColumnWidth > minWidth));
8025        if(shouldChange) {
8026            var frozenColumn = columnHeader.hasClass('ui-frozen-column'),
8027            theadClone = frozenColumn ? this.frozenTheadClone : this.scrollTheadClone,
8028            originalTable = frozenColumn ? this.frozenThead.parent() : this.scrollThead.parent(),
8029            cloneTable = theadClone.parent(),
8030            footerCols = frozenColumn ? this.frozenFooterCols : this.scrollFooterCols,
8031            footerTable = frozenColumn ? this.frozenFooterTable:  this.scrollFooterTable,
8032            $this = this;
8033
8034            if(expandMode) {
8035                if(lastFrozen) {
8036                    this.frozenLayout.width(this.frozenLayout.width() + change);
8037                }
8038
8039                var originalTableWidth = originalTable.width(),
8040                cloneTableWidth = cloneTable.width(),
8041                footerTableWidth = footerTable.width();
8042
8043                //header
8044                originalTable.width(originalTableWidth + change);
8045
8046                //body
8047                cloneTable.width(cloneTableWidth + change);
8048
8049                //footer
8050                footerTable.width(footerTableWidth + change);
8051
8052                setTimeout(function() {
8053                    columnHeader.width(newWidth);
8054
8055                    if($this.hasColumnGroup) {
8056                        theadClone.find('> tr:first').children('th').eq(colIndex).width(newWidth);                          //body
8057                        footerTable.find('> tfoot > tr:first').children('th').eq(colIndex).width(newWidth);                 //footer
8058                    }
8059                    else {
8060                        theadClone.find(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).width(newWidth);     //body
8061                        footerCols.eq(colIndex).width(newWidth);                                                            //footer
8062                    }
8063                }, 1);
8064
8065
8066            }
8067            else {
8068                if(lastFrozen) {
8069                    this.frozenLayout.width(this.frozenLayout.width() + change);
8070                }
8071
8072                columnHeader.width(newWidth);
8073                nextColumnHeader.width(nextColumnWidth);
8074
8075                if(this.hasColumnGroup) {
8076                    //body
8077                    theadClone.find('> tr:first').children('th').eq(colIndex).width(newWidth);
8078                    theadClone.find('> tr:first').children('th').eq(colIndex + 1).width(nextColumnWidth);
8079
8080                    //footer
8081                    footerTable.find('> tfoot > tr:first').children('th').eq(colIndex).width(newWidth);
8082                    footerTable.find('> tfoot > tr:first').children('th').eq(colIndex + 1).width(nextColumnWidth);
8083                }
8084                else {
8085                    theadClone.find(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).width(newWidth);
8086                    theadClone.find(PrimeFaces.escapeClientId(nextColumnHeader.attr('id') + '_clone')).width(nextColumnWidth);
8087
8088                    if(footerCols.length > 0) {
8089                        var footerCol = footerCols.eq(colIndex),
8090                        nextFooterCol = footerCol.next();
8091
8092                        footerCol.width(newWidth);
8093                        nextFooterCol.width(nextColumnWidth);
8094                    }
8095                }
8096            }
8097        }
8098    },
8099
8100    //@Override
8101    hasColGroup: function() {
8102        return this.frozenThead.children('tr').length > 1 || this.scrollThead.children('tr').length > 1;
8103    },
8104
8105    //@Override
8106    addGhostRow: function() {
8107        this._addGhostRow(this.frozenTbody, this.frozenThead, this.frozenTheadClone, this.frozenFooter.find('table'), 'ui-frozen-column');
8108        this._addGhostRow(this.scrollTbody, this.scrollThead, this.scrollTheadClone, this.scrollFooterTable);
8109    },
8110
8111    _addGhostRow: function(body, header, headerClone, footerTable, columnClass) {
8112        var dataColumns = body.find('tr:first').children('td'),
8113        dataColumnsCount = dataColumns.length,
8114        columnMarkup = '',
8115        columnStyleClass = columnClass ? 'ui-resizable-column ' + columnClass : 'ui-resizable-column';
8116
8117        for(var i = 0; i < dataColumnsCount; i++) {
8118            columnMarkup += '<th style="height:0px;border-bottom-width: 0px;border-top-width: 0px;padding-top: 0px;padding-bottom: 0px;outline: 0 none;width:' + dataColumns.eq(i).width() + 'px" class="' + columnStyleClass + '"></th>';
8119        }
8120
8121        header.prepend('<tr>' + columnMarkup + '</tr>');
8122
8123        if(this.cfg.scrollable) {
8124            headerClone.prepend('<tr>' + columnMarkup + '</tr>');
8125            footerTable.children('tfoot').prepend('<tr>' + columnMarkup + '</tr>');
8126        }
8127    },
8128
8129    getFocusableTbody: function() {
8130        return this.tbody.eq(0);
8131    },
8132
8133    highlightFocusedRow: function() {
8134        this._super();
8135        this.getTwinRow(this.focusedRow).addClass('ui-state-hover');
8136    },
8137
8138    unhighlightFocusedRow: function() {
8139        this._super();
8140        this.getTwinRow(this.focusedRow).removeClass('ui-state-hover');
8141    },
8142
8143    assignFocusedRow: function(row) {
8144        this._super(row);
8145
8146        if(!row.parent().attr('tabindex')) {
8147            this.frozenTbody.trigger('focus');
8148        }
8149    },
8150
8151    //@Override
8152    saveColumnOrder: function() {
8153        var columnIds = [],
8154        columns = $(this.jqId + '_frozenThead:first th,' + this.jqId + '_scrollableThead:first th');
8155
8156        columns.each(function(i, item) {
8157            columnIds.push($(item).attr('id'));
8158        });
8159
8160        this.orderStateHolder.val(columnIds.join(','));
8161    },
8162
8163    //Override
8164    resetVirtualScrollBody: function() {
8165        this.scrollBodyTable.css('top', '0px');
8166        this.frozenBodyTable.css('top', '0px');
8167        this.scrollBody.scrollTop(0);
8168        this.frozenBody.scrollTop(0);
8169        this.clearScrollState();
8170    },
8171
8172    //Override
8173    groupRows: function() {
8174        var scrollRows = this.scrollTbody.children('tr'),
8175        frozenRows = this.frozenTbody.children('tr');
8176
8177        for (var i = 0; i < this.cfg.groupColumnIndexes.length; i++) {
8178            var groupColumnIndex = this.cfg.groupColumnIndexes[i];
8179
8180            if (groupColumnIndex >= this.cfg.frozenColumns) {
8181                this.groupRow(groupColumnIndex - this.cfg.frozenColumns, scrollRows);
8182            }
8183            else {
8184                this.groupRow(groupColumnIndex, frozenRows);
8185            }
8186        }
8187
8188        scrollRows.children('td.ui-duplicated-column').remove();
8189        frozenRows.children('td.ui-duplicated-column').remove();
8190    }
8191});
8192
8193/**
8194 * PrimeFaces Dialog Widget
8195 */
8196PrimeFaces.widget.Dialog = PrimeFaces.widget.DynamicOverlayWidget.extend({
8197
8198    init: function(cfg) {
8199        this._super(cfg);
8200
8201        this.content = this.jq.children('.ui-dialog-content');
8202        this.titlebar = this.jq.children('.ui-dialog-titlebar');
8203        this.footer = this.jq.find('.ui-dialog-footer');
8204        this.icons = this.titlebar.children('.ui-dialog-titlebar-icon');
8205        this.closeIcon = this.titlebar.children('.ui-dialog-titlebar-close');
8206        this.minimizeIcon = this.titlebar.children('.ui-dialog-titlebar-minimize');
8207        this.maximizeIcon = this.titlebar.children('.ui-dialog-titlebar-maximize');
8208        this.cfg.absolutePositioned = this.jq.hasClass('ui-dialog-absolute');
8209        this.jqEl = this.jq[0];
8210
8211        this.positionInitialized = false;
8212
8213        //configuration
8214        this.cfg.width = this.cfg.width||'auto';
8215        this.cfg.height = this.cfg.height||'auto';
8216        this.cfg.draggable = this.cfg.draggable === false ? false : true;
8217        this.cfg.resizable = this.cfg.resizable === false ? false : true;
8218        this.cfg.minWidth = this.cfg.minWidth||150;
8219        this.cfg.minHeight = this.cfg.minHeight||this.titlebar.outerHeight();
8220        this.cfg.my = this.cfg.my||'center';
8221        this.cfg.position = this.cfg.position||'center';
8222        this.parent = this.jq.parent();
8223
8224        this.initSize();
8225        
8226        //events
8227        this.bindEvents();
8228
8229        if(this.cfg.draggable) {
8230            this.setupDraggable();
8231        }
8232
8233        if(this.cfg.resizable){
8234            this.setupResizable();
8235        }
8236
8237        //docking zone
8238        if($(document.body).children('.ui-dialog-docking-zone').length === 0) {
8239            $(document.body).append('<div class="ui-dialog-docking-zone"></div>');
8240        }
8241
8242        //aria
8243        this.applyARIA();
8244
8245        if(this.cfg.visible){
8246            this.show();
8247        }
8248
8249        if(this.cfg.responsive) {
8250            this.bindResizeListener();
8251        }
8252    },
8253
8254    //@Override
8255    refresh: function(cfg) {
8256        this.positionInitialized = false;
8257        this.loaded = false;
8258
8259        $(document).off('keydown.dialog_' + cfg.id);
8260
8261        if(this.minimized) {
8262            var dockingZone = $(document.body).children('.ui-dialog-docking-zone');
8263            if(dockingZone.length && dockingZone.children(this.jqId).length) {
8264                this.removeMinimize();
8265                dockingZone.children(this.jqId).remove();
8266            }
8267        }
8268
8269        this.minimized = false;
8270        this.maximized = false;
8271
8272        this._super(cfg);
8273    },
8274
8275    initSize: function() {
8276        this.jq.css({
8277            'width': this.cfg.width,
8278            'height': 'auto'
8279        });
8280
8281        this.content.height(this.cfg.height);
8282
8283        if(this.cfg.fitViewport) {
8284            this.fitViewport();
8285        }
8286    },
8287
8288    fitViewport: function() {
8289        var windowHeight = $(window).height();
8290
8291        var margin = this.jq.outerHeight(true) - this.jq.outerHeight();
8292        var headerHeight = this.titlebar.outerHeight(true);
8293        var contentPadding = this.content.innerHeight() - this.content.height();
8294        var footerHeight = this.footer.outerHeight(true) || 0;
8295
8296        var maxHeight = windowHeight - (margin + headerHeight + contentPadding + footerHeight);
8297
8298        this.content.css('max-height', maxHeight + 'px');
8299    },
8300
8301    //@override
8302    getModalTabbables: function(){
8303        return this.jq.find(':tabbable').add(this.footer.find(':tabbable'));
8304    },
8305
8306    /**
8307     * Show the dialog with optional animation duration.
8308     * 
8309     * @param [duration] Durations are given in milliseconds; higher values indicate slower animations, not faster ones. 
8310     *                 The strings 'fast' and 'slow' can be supplied to indicate durations of 200 and 600 milliseconds, 
8311     *                 respectively.
8312     */
8313    show: function(duration) {
8314        if(this.isVisible()) {
8315            return;
8316        }
8317
8318        if(!this.loaded && this.cfg.dynamic) {
8319            this.loadContents();
8320        }
8321        else {
8322            if (this.positionInitialized === false) {
8323                this.jqEl.style.visibility = "hidden";
8324                this.jqEl.style.display = "block";
8325                this.initPosition();
8326                this.jqEl.style.display = "none";
8327                this.jqEl.style.visibility = "visible";
8328            }
8329
8330            this._show(duration);
8331        }
8332    },
8333
8334    _show: function(duration) {
8335        this.moveToTop();
8336
8337        //offset
8338        if(this.cfg.absolutePositioned) {
8339            var winScrollTop = $(window).scrollTop();
8340            this.jq.css('top', parseFloat(this.jq.css('top')) + (winScrollTop - this.lastScrollTop) + 'px');
8341            this.lastScrollTop = winScrollTop;
8342        }
8343
8344        var animated = this.cfg.showEffect;
8345        if(animated) {
8346            var $this = this;
8347
8348            this.jq.show(this.cfg.showEffect, duration, 'normal', function() {
8349                $this.postShow();
8350            });
8351        }
8352        else {
8353            //display dialog
8354            this.jq.show(duration);
8355
8356            this.postShow();
8357        }
8358
8359        if(this.cfg.modal) {
8360            this.enableModality();
8361        }
8362    },
8363
8364    postShow: function() {
8365        if (this.cfg.fitViewport) {
8366            this.fitViewport();
8367        }
8368        
8369        this.callBehavior('open');
8370
8371        PrimeFaces.invokeDeferredRenders(this.id);
8372
8373        //execute user defined callback
8374        if(this.cfg.onShow) {
8375            this.cfg.onShow.call(this);
8376        }
8377
8378        this.jq.attr({
8379            'aria-hidden': false
8380            ,'aria-live': 'polite'
8381        });
8382
8383        this.applyFocus();
8384    },
8385
8386    /**
8387     * Hide the dialog with optional duration animation.
8388     * 
8389     * @param [duration] Durations are given in milliseconds; higher values indicate slower animations, not faster ones. 
8390     *                 The strings 'fast' and 'slow' can be supplied to indicate durations of 200 and 600 milliseconds, 
8391     *                 respectively.
8392     */
8393    hide: function(duration) {
8394        if(!this.isVisible()) {
8395            return;
8396        }
8397
8398        var animated = this.cfg.hideEffect;
8399        if(animated) {
8400            var $this = this;
8401
8402            this.jq.hide(this.cfg.hideEffect, duration, 'normal', function() {
8403                if($this.cfg.modal) {
8404                    $this.disableModality();
8405                }
8406                $this.onHide();
8407            });
8408        }
8409        else {
8410            this.jq.hide();
8411            if(this.cfg.modal) {
8412                this.disableModality();
8413            }
8414            this.onHide(duration);
8415        }
8416    },
8417
8418    applyFocus: function() {
8419        if(this.cfg.focus)
8420        	PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.cfg.focus).focus();
8421        else
8422            this.jq.find(':not(:submit):not(:button):not(:radio):not(:checkbox):input:visible:enabled:first').focus();
8423    },
8424
8425    bindEvents: function() {
8426        var $this = this;
8427
8428        //Move dialog to top if target is not a trigger for a PrimeFaces overlay
8429        this.jq.mousedown(function(e) {
8430            if(!$(e.target).data('primefaces-overlay-target')) {
8431                $this.moveToTop();
8432            }
8433        });
8434
8435        this.icons.on('mouseover', function() {
8436            $(this).addClass('ui-state-hover');
8437        }).on('mouseout', function() {
8438            $(this).removeClass('ui-state-hover');
8439        }).on('focus', function() {
8440            $(this).addClass('ui-state-focus');
8441        }).on('blur', function() {
8442            $(this).removeClass('ui-state-focus');
8443        });
8444
8445        this.closeIcon.on('click', function(e) {
8446            $this.hide();
8447            e.preventDefault();
8448        });
8449
8450        this.maximizeIcon.click(function(e) {
8451            $this.toggleMaximize();
8452            e.preventDefault();
8453        });
8454
8455        this.minimizeIcon.click(function(e) {
8456            $this.toggleMinimize();
8457            e.preventDefault();
8458        });
8459
8460        if(this.cfg.closeOnEscape) {
8461            $(document).on('keydown.dialog_' + this.id, function(e) {
8462                var keyCode = $.ui.keyCode,
8463                active = parseInt($this.jq.css('z-index')) === PrimeFaces.zindex;
8464
8465                if(e.which === keyCode.ESCAPE && $this.isVisible() && active) {
8466                    $this.hide();
8467                };
8468            });
8469        }
8470    },
8471
8472    setupDraggable: function() {
8473        var $this = this;
8474
8475        this.jq.draggable({
8476            cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
8477            handle: '.ui-dialog-titlebar',
8478            containment : $this.cfg.absolutePositioned ? 'document' : 'window',
8479            stop: function( event, ui ) {
8480                if($this.hasBehavior('move')) {
8481                    var ext = {
8482                        params: [
8483                            {name: $this.id + '_top', value: ui.offset.top},
8484                            {name: $this.id + '_left', value: ui.offset.left}
8485                        ]
8486                    };
8487                    $this.callBehavior('move', ext);
8488                }
8489            }
8490        });
8491    },
8492
8493    setupResizable: function() {
8494        var $this = this;
8495
8496        this.jq.resizable({
8497            handles : 'n,s,e,w,ne,nw,se,sw',
8498            minWidth : this.cfg.minWidth,
8499            minHeight : this.cfg.minHeight,
8500            alsoResize : this.content,
8501            containment: 'document',
8502            start: function(event, ui) {
8503                $this.jq.data('offset', $this.jq.offset());
8504
8505                if($this.cfg.hasIframe) {
8506                    $this.iframeFix = $('<div style="position:absolute;background-color:transparent;width:100%;height:100%;top:0;left:0;"></div>').appendTo($this.content);
8507                }
8508
8509                if ($this.hasBehavior('resizeStart')) {
8510                    var ext = {
8511                        params: [
8512                            {name: $this.id + '_width', value: ui.size.width},
8513                            {name: $this.id + '_height', value: ui.size.height}
8514                        ]
8515                    };
8516                    $this.callBehavior('resizeStart', ext);
8517                }
8518            },
8519            stop: function(event, ui) {
8520                $this.jq.css('position', 'fixed');
8521
8522                if($this.cfg.hasIframe) {
8523                    $this.iframeFix.remove();
8524                }
8525
8526                if ($this.hasBehavior('resizeStop')) {
8527                    var ext = {
8528                        params: [
8529                            {name: $this.id + '_width', value: ui.size.width},
8530                            {name: $this.id + '_height', value: ui.size.height}
8531                        ]
8532                    };
8533                    $this.callBehavior('resizeStop', ext);
8534                }
8535            }
8536        });
8537
8538        this.resizers = this.jq.children('.ui-resizable-handle');
8539    },
8540    
8541    /**
8542     * Reset the dialog position based on the configured "position".
8543     */
8544    resetPosition: function() {
8545       this.initPosition();
8546    },
8547
8548    initPosition: function() {
8549        var $this = this;
8550
8551        //reset
8552        this.jq.css({left:0,top:0});
8553
8554        if(/(center|left|top|right|bottom)/.test(this.cfg.position)) {
8555            this.cfg.position = this.cfg.position.replace(',', ' ');
8556
8557            this.jq.position({
8558                        my: this.cfg.my
8559                        ,at: this.cfg.position
8560                        ,collision: 'fit'
8561                        ,of: window
8562                        //make sure dialog stays in viewport
8563                        ,using: function(pos) {
8564                            var l = pos.left < 0 ? 0 : pos.left,
8565                            t = pos.top < 0 ? 0 : pos.top,
8566                            scrollTop = $(window).scrollTop();
8567
8568                            //offset
8569                            if($this.cfg.absolutePositioned) {
8570                                t += scrollTop;
8571                                $this.lastScrollTop = scrollTop;
8572                            }
8573
8574                            $(this).css({
8575                                left: l
8576                                ,top: t
8577                            });
8578                        }
8579                    });
8580        }
8581        else {
8582            var coords = this.cfg.position.split(','),
8583            x = $.trim(coords[0]),
8584            y = $.trim(coords[1]);
8585
8586            this.jq.offset({
8587                left: x
8588                ,top: y
8589            });
8590        }
8591
8592        this.positionInitialized = true;
8593    },
8594
8595    onHide: function(event, ui) {
8596        this.callBehavior('close');
8597
8598        this.jq.attr({
8599            'aria-hidden': true
8600            ,'aria-live': 'off'
8601        });
8602
8603        if(this.cfg.onHide) {
8604            this.cfg.onHide.call(this, event, ui);
8605        }
8606    },
8607
8608    moveToTop: function() {
8609        this.jq.css('z-index', ++PrimeFaces.zindex);
8610    },
8611
8612    toggleMaximize: function() {
8613        if(this.minimized) {
8614            this.toggleMinimize();
8615        }
8616
8617        if(this.maximized) {
8618            this.jq.removeClass('ui-dialog-maximized');
8619            this.restoreState();
8620
8621            this.maximizeIcon.children('.ui-icon').removeClass('ui-icon-newwin').addClass('ui-icon-extlink');
8622            this.maximized = false;
8623
8624            this.callBehavior('restoreMaximize');
8625        }
8626        else {
8627            this.saveState();
8628
8629            var win = $(window);
8630
8631            this.jq.addClass('ui-dialog-maximized').css({
8632                'width': win.width() - 6
8633                ,'height': win.height()
8634            }).offset({
8635                top: win.scrollTop()
8636                ,left: win.scrollLeft()
8637            });
8638
8639            //maximize content
8640            var contentPadding = this.content.innerHeight() - this.content.height();
8641            this.content.css({
8642                width: 'auto',
8643                height: this.jq.height() - this.titlebar.outerHeight() - contentPadding
8644            });
8645
8646            this.maximizeIcon.removeClass('ui-state-hover').children('.ui-icon').removeClass('ui-icon-extlink').addClass('ui-icon-newwin');
8647            this.maximized = true;
8648
8649            this.callBehavior('maximize');
8650        }
8651    },
8652
8653    toggleMinimize: function() {
8654        var animate = true,
8655        dockingZone = $(document.body).children('.ui-dialog-docking-zone');
8656
8657        if(this.maximized) {
8658            this.toggleMaximize();
8659            animate = false;
8660        }
8661
8662        var $this = this;
8663
8664        if(this.minimized) {
8665            this.removeMinimize();
8666
8667            this.callBehavior('restoreMinimize');
8668        }
8669        else {
8670            this.saveState();
8671
8672            if(animate) {
8673                this.jq.effect('transfer', {
8674                                to: dockingZone
8675                                ,className: 'ui-dialog-minimizing'
8676                                }, 500,
8677                                function() {
8678                                    $this.dock(dockingZone);
8679                                    $this.jq.addClass('ui-dialog-minimized');
8680                                });
8681            }
8682            else {
8683                this.dock(dockingZone);
8684                this.jq.addClass('ui-dialog-minimized');
8685            }
8686        }
8687    },
8688
8689    dock: function(zone) {
8690        zone.css('z-index', this.jq.css('z-index'));
8691        this.jq.appendTo(zone).css('position', 'static');
8692        this.jq.css({'height':'auto', 'width':'auto', 'float': 'left'});
8693        this.content.hide();
8694        this.footer.hide();
8695        this.minimizeIcon.removeClass('ui-state-hover').children('.ui-icon').removeClass('ui-icon-minus').addClass('ui-icon-plus');
8696        this.minimized = true;
8697
8698        if(this.cfg.resizable) {
8699            this.resizers.hide();
8700        }
8701
8702        this.callBehavior('minimize');
8703    },
8704
8705    saveState: function() {
8706        this.state = {
8707            width: this.jq.width(),
8708            height: this.jq.height(),
8709            contentWidth: this.content.width(),
8710            contentHeight: this.content.height()
8711        };
8712
8713        var win = $(window);
8714        this.state.offset = this.jq.offset();
8715        this.state.windowScrollLeft = win.scrollLeft();
8716        this.state.windowScrollTop = win.scrollTop();
8717    },
8718
8719    restoreState: function() {
8720        this.jq.width(this.state.width).height(this.state.height);
8721        this.content.width(this.state.contentWidth).height(this.state.contentHeight);
8722
8723        var win = $(window);
8724        this.jq.offset({
8725                top: this.state.offset.top + (win.scrollTop() - this.state.windowScrollTop)
8726                ,left: this.state.offset.left + (win.scrollLeft() - this.state.windowScrollLeft)
8727        });
8728    },
8729
8730    loadContents: function() {
8731        var $this = this,
8732        options = {
8733            source: this.id,
8734            process: this.id,
8735            update: this.id,
8736            params: [
8737                {name: this.id + '_contentLoad', value: true}
8738            ],
8739            onsuccess: function(responseXML, status, xhr) {
8740                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
8741                        widget: $this,
8742                        handle: function(content) {
8743                            this.content.html(content);
8744                        }
8745                    });
8746
8747                return true;
8748            },
8749            oncomplete: function() {
8750                $this.loaded = true;
8751                $this.show();
8752            }
8753        };
8754
8755        if(this.hasBehavior('loadContent')) {
8756            this.callBehavior('loadContent', options);
8757        }
8758        else {
8759            PrimeFaces.ajax.Request.handle(options);
8760        }
8761    },
8762
8763    applyARIA: function() {
8764        this.jq.attr({
8765            'role': 'dialog'
8766            ,'aria-describedby': this.id + '_content'
8767            ,'aria-hidden': !this.cfg.visible
8768            ,'aria-modal': this.cfg.modal
8769        });
8770        
8771        // GitHub #4727
8772        var title = this.id + '_title';
8773        if ($(PrimeFaces.escapeClientId(title)).length) {
8774            this.jq.attr('aria-labelledby', title);
8775        }
8776
8777        this.titlebar.children('a.ui-dialog-titlebar-icon').attr('role', 'button');
8778    },
8779
8780    isVisible: function() {
8781        return this.jq.is(':visible');
8782    },
8783
8784    bindResizeListener: function() {
8785        var $this = this;
8786
8787        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', null, function() {
8788            if ($this.cfg.fitViewport) {
8789                $this.fitViewport();
8790            }
8791
8792            if ($this.isVisible()) {
8793                // instant reinit position
8794                $this.initPosition();
8795            }
8796            else {
8797                // reset, so the dialog will be positioned again when showing the dialog next time
8798                $this.positionInitialized = false;
8799            }
8800        });
8801    },
8802
8803    removeMinimize: function() {
8804        this.jq.appendTo(this.parent).removeClass('ui-dialog-minimized').css({'position':'fixed', 'float':'none'});
8805        this.restoreState();
8806        this.content.show();
8807        this.footer.show();
8808        this.minimizeIcon.removeClass('ui-state-hover').children('.ui-icon').removeClass('ui-icon-plus').addClass('ui-icon-minus');
8809        this.minimized = false;
8810
8811        if(this.cfg.resizable) {
8812            this.resizers.show();
8813        }
8814    }
8815
8816});
8817
8818/**
8819 * PrimeFaces ConfirmDialog Widget
8820 */
8821PrimeFaces.widget.ConfirmDialog = PrimeFaces.widget.Dialog.extend({
8822
8823    init: function(cfg) {
8824        cfg.draggable = false;
8825        cfg.resizable = false;
8826        cfg.modal = true;
8827
8828        if (!cfg.appendTo && cfg.global) {
8829        	cfg.appendTo = '@(body)';
8830        }
8831
8832        this._super(cfg);
8833
8834        this.title = this.titlebar.children('.ui-dialog-title');
8835        this.message = this.content.children('.ui-confirm-dialog-message');
8836        this.icon = this.content.children('.ui-confirm-dialog-severity');
8837
8838        if(this.cfg.global) {
8839            PrimeFaces.confirmDialog = this;
8840
8841            this.jq.on('click.ui-confirmdialog', '.ui-confirmdialog-yes, .ui-confirmdialog-no', null, function(e) {
8842                var el = $(this);
8843
8844                if(el.hasClass('ui-confirmdialog-yes') && PrimeFaces.confirmSource) {
8845                    var id = PrimeFaces.confirmSource.get(0);
8846                    var js = PrimeFaces.confirmSource.data('pfconfirmcommand');
8847
8848                    PrimeFaces.csp.executeEvent(id, js, e);
8849
8850                    PrimeFaces.confirmDialog.hide();
8851                    PrimeFaces.confirmSource = null;
8852                }
8853                else if(el.hasClass('ui-confirmdialog-no')) {
8854                    PrimeFaces.confirmDialog.hide();
8855                    PrimeFaces.confirmSource = null;
8856                }
8857
8858                e.preventDefault();
8859            });
8860        }
8861    },
8862
8863    applyFocus: function() {
8864        this.jq.find(':button,:submit').filter(':visible:enabled').eq(0).focus();
8865    },
8866
8867    showMessage: function(msg) {
8868        if(msg.beforeShow) {
8869            PrimeFaces.csp.eval(msg.beforeShow);
8870        }
8871
8872        var icon = (msg.icon === 'null') ? 'ui-icon-alert' : msg.icon;
8873        this.icon.removeClass().addClass('ui-icon ui-confirm-dialog-severity ' + icon);
8874
8875        if(msg.header)
8876            this.title.text(msg.header);
8877
8878        if(msg.message){
8879            if (msg.escape){
8880                this.message.text(msg.message);
8881            }
8882            else {
8883            	this.message.html(msg.message);
8884            }
8885        }
8886
8887        this.show();
8888    }
8889
8890});
8891
8892/**
8893 * PrimeFaces Dynamic Dialog Widget for Dialog Framework
8894 */
8895PrimeFaces.widget.DynamicDialog = PrimeFaces.widget.Dialog.extend({
8896
8897    //@Override
8898    show: function() {
8899        if(this.jq.hasClass('ui-overlay-visible')) {
8900            return;
8901        }
8902
8903        if(this.positionInitialized === false) {
8904            this.initPosition();
8905        }
8906
8907        this._show();
8908    },
8909
8910    //@Override
8911    _show: function() {
8912        //replace visibility hidden with display none for effect support, toggle marker class
8913        this.jq.removeClass('ui-overlay-hidden').addClass('ui-overlay-visible').css({
8914            'display':'none'
8915            ,'visibility':'visible'
8916        });
8917
8918        this.moveToTop();
8919
8920        this.jq.show();
8921
8922        if(this.cfg.height != "auto") {
8923            this.content.height(this.jq.outerHeight() - this.titlebar.outerHeight(true));
8924        }
8925
8926        this.postShow();
8927
8928        if(this.cfg.modal) {
8929            this.enableModality();
8930        }
8931    },
8932
8933    //@Override
8934    initSize: function() {
8935        this.jq.css({
8936            'width': this.cfg.width,
8937            'height': this.cfg.height
8938        });
8939
8940        if(this.cfg.fitViewport) {
8941            this.fitViewport();
8942        }
8943    }
8944
8945});
8946
8947/**
8948 * PrimeFaces Draggable Widget
8949 */
8950PrimeFaces.widget.Draggable = PrimeFaces.widget.BaseWidget.extend({
8951    
8952    init: function(cfg) {
8953        this.cfg = cfg;
8954        this.id = this.cfg.id;
8955        this.jqId = PrimeFaces.escapeClientId(this.id);
8956        this.jq = $(PrimeFaces.escapeClientId(this.cfg.target));
8957        this.cfg.cancel = this.cfg.cancel || "input,textarea,button,select,option";
8958
8959        if(this.cfg.appendTo) {
8960            this.cfg.appendTo = PrimeFaces.expressions.SearchExpressionFacade.resolve
8960ComponentsAsSelector(this.cfg.appendTo);
8961        }
8962        
8963        var $this = this;
8964
8965        this.cfg.start = function(event, ui) {
8966            if($this.cfg.onStart) {
8967                $this.cfg.onStart.call($this, event, ui);
8968            }
8969        };
8970        
8971        this.cfg.stop = function(event, ui) {
8972            if($this.cfg.onStop) {
8973                $this.cfg.onStop.call($this, event, ui);
8974            }
8975        };
8976        
8977        this.jq.draggable(this.cfg);
8978        
8979        this.removeScriptElement(this.id);
8980    }
8981    
8982});
8983
8984/**
8985 * PrimeFaces Droppable Widget
8986 */
8987PrimeFaces.widget.Droppable = PrimeFaces.widget.BaseWidget.extend({
8988    
8989    init: function(cfg) {
8990        this.cfg = cfg;
8991        this.id = this.cfg.id;
8992        this.jqId = PrimeFaces.escapeClientId(this.id);
8993        this.jq = $(PrimeFaces.escapeClientId(this.cfg.target));
8994
8995        this.bindDropListener();
8996
8997        this.jq.droppable(this.cfg);
8998        
8999        this.removeScriptElement(this.id);
9000    },
9001    
9002    bindDropListener: function() {
9003        var _self = this;
9004
9005        this.cfg.drop = function(event, ui) {
9006            if(_self.cfg.onDrop) {
9007                _self.cfg.onDrop.call(_self, event, ui);
9008            }
9009            if(_self.cfg.behaviors) {
9010                var dropBehavior = _self.cfg.behaviors['drop'];
9011
9012                if(dropBehavior) {
9013                    var ext = {
9014                        params: [
9015                            {name: _self.id + '_dragId', value: ui.draggable.attr('id')},
9016                            {name: _self.id + '_dropId', value: _self.cfg.target}
9017                        ]
9018                    };
9019
9020                    dropBehavior.call(_self, ext);
9021                }
9022            }
9023        };
9024    }
9025    
9026});
9027/**
9028 * PrimeFaces Effect Widget
9029 */
9030PrimeFaces.widget.Effect = PrimeFaces.widget.BaseWidget.extend({
9031    
9032    init: function(cfg) {
9033        this.cfg = cfg;
9034        this.id = this.cfg.id;
9035        this.jqId = PrimeFaces.escapeClientId(this.id);
9036        this.source = $(PrimeFaces.escapeClientId(this.cfg.source));
9037        var _self = this;
9038
9039        this.runner = function() {
9040            //avoid queuing multiple runs
9041            if(_self.timeoutId) {
9042                clearTimeout(_self.timeoutId);
9043            }
9044
9045            _self.timeoutId = setTimeout(_self.cfg.fn, _self.cfg.delay);
9046        };
9047
9048        if(this.cfg.event == 'load') {
9049            this.runner.call();
9050        } 
9051        else {
9052            this.source.on(this.cfg.event, this.runner);
9053        }
9054        
9055        this.removeScriptElement(this.id);
9056    }
9057    
9058});
9059/**
9060 * PrimeFaces Fieldset Widget
9061 */
9062PrimeFaces.widget.Fieldset = PrimeFaces.widget.BaseWidget.extend({
9063
9064    init: function(cfg) {
9065        this._super(cfg);
9066
9067        this.legend = this.jq.children('.ui-fieldset-legend');
9068
9069        var $this = this;
9070
9071        if(this.cfg.toggleable) {
9072            this.content = this.jq.children('.ui-fieldset-content');
9073            this.toggler = this.legend.children('.ui-fieldset-toggler');
9074            this.stateHolder = $(this.jqId + '_collapsed');
9075
9076            //Add clickable legend state behavior
9077            this.legend.on('click', function(e) {
9078                $this.toggle(e);
9079            })
9080            .on('mouseover', function() {
9081                $this.legend.toggleClass('ui-state-hover');
9082            })
9083            .on('mouseout', function() {
9084                $this.legend.toggleClass('ui-state-hover');
9085            })
9086            .on('mousedown', function() {
9087                $this.legend.toggleClass('ui-state-active');
9088            })
9089            .on('mouseup', function() {
9090                $this.legend.toggleClass('ui-state-active');
9091            })
9092            .on('focus', function() {
9093                $this.legend.toggleClass('ui-state-focus');
9094            })
9095            .on('blur', function() {
9096                $this.legend.toggleClass('ui-state-focus');
9097            })
9098            .on('keydown', function(e) {
9099                var key = e.which,
9100                keyCode = $.ui.keyCode;
9101
9102                if((key === keyCode.ENTER)) {
9103                    $this.toggle(e);
9104                    e.preventDefault();
9105                }
9106            });
9107        }
9108    },
9109
9110    /**
9111     * Toggles the content
9112     */
9113    toggle: function(e) {
9114        this.updateToggleState(this.cfg.collapsed);
9115
9116        var $this = this;
9117
9118        this.content.slideToggle(this.cfg.toggleSpeed, 'easeInOutCirc', function() {
9119            $this.callBehavior('toggle');
9120        });
9121
9122        PrimeFaces.invokeDeferredRenders(this.id);
9123    },
9124
9125    /**
9126     * Updates the visual toggler state and saves state
9127     */
9128    updateToggleState: function(collapsed) {
9129        if(collapsed)
9130            this.toggler.removeClass('ui-icon-plusthick').addClass('ui-icon-minusthick');
9131        else
9132            this.toggler.removeClass('ui-icon-minusthick').addClass('ui-icon-plusthick');
9133
9134        this.cfg.collapsed = !collapsed;
9135
9136        this.stateHolder.val(!collapsed);
9137    }
9138
9139});
9140/**
9141 * PrimeFaces InputText Widget
9142 */
9143PrimeFaces.widget.InputText = PrimeFaces.widget.BaseWidget.extend({
9144
9145    init: function(cfg) {
9146        this._super(cfg);
9147
9148        PrimeFaces.skinInput(this.jq);
9149
9150        //Counter
9151        if(this.cfg.counter) {
9152            this.counter = this.cfg.counter ? $(PrimeFaces.escapeClientId(this.cfg.counter)) : null;
9153            this.cfg.counterTemplate = this.cfg.counterTemplate||'{0}';
9154            this.updateCounter();
9155
9156            if(this.counter) {
9157                var $this = this;
9158                this.jq.on('input.inputtext-counter', function(e) {
9159                    $this.updateCounter();
9160                });
9161            }
9162        }
9163    },
9164
9165    disable: function() {
9166        this.jq.prop('disabled', true).addClass('ui-state-disabled');
9167    },
9168
9169    enable: function() {
9170        this.jq.prop('disabled', false).removeClass('ui-state-disabled');
9171    },
9172
9173    updateCounter: function() {
9174        var value = this.normalizeNewlines(this.jq.val()),
9175        length = value.length;
9176
9177        if(this.counter && this.cfg.maxlength) {
9178            var remaining = this.cfg.maxlength - length;
9179            if(remaining < 0) {
9180                remaining = 0;
9181            }
9182
9183            var counterText = this.cfg.counterTemplate
9184                    .replace('{0}', remaining)
9185                    .replace('{1}', length)
9186                    .replace('{2}', this.cfg.maxlength);
9187
9188            this.counter.text(counterText);
9189        }
9190    },
9191
9192    normalizeNewlines: function(text) {
9193        return text.replace(/(\r\n|\r|\n)/g, '\r\n');
9194    }
9195});
9196
9197/**
9198 * PrimeFaces InputTextarea Widget
9199 */
9200PrimeFaces.widget.InputTextarea = PrimeFaces.widget.DeferredWidget.extend({
9201
9202    init: function(cfg) {
9203        this._super(cfg);
9204
9205        if(this.cfg.autoResize)
9206            this.renderDeferred();
9207        else
9208            this._render();
9209    },
9210
9211    _render: function() {
9212        //Visuals
9213        PrimeFaces.skinInput(this.jq);
9214
9215        //autoComplete
9216        if(this.cfg.autoComplete) {
9217            this.setupAutoComplete();
9218        }
9219
9220        //Counter
9221        if(this.cfg.counter) {
9222            this.counter = this.cfg.counter ? $(PrimeFaces.escapeClientId(this.cfg.counter)) : null;
9223            this.cfg.counterTemplate = this.cfg.counterTemplate||'{0}';
9224            this.updateCounter();
9225
9226            if(this.counter) {
9227                var $this = this;
9228                this.jq.on('input.inputtextarea-counter', function(e) {
9229                    $this.updateCounter();
9230                });
9231            }
9232        }
9233
9234        //maxLength
9235        if(this.cfg.maxlength) {
9236            this.applyMaxlength();
9237        }
9238
9239        //autoResize
9240        if(this.cfg.autoResize) {
9241            this.setupAutoResize();
9242        }
9243    },
9244
9245    //@override
9246    refresh: function(cfg) {
9247        //remove autocomplete panel
9248        if(cfg.autoComplete) {
9249            $(PrimeFaces.escapeClientId(cfg.id + '_panel')).remove();
9250        }
9251
9252        this._super(cfg);
9253    },
9254
9255    setupAutoResize: function() {
9256        autosize(this.jq);
9257    },
9258
9259    applyMaxlength: function() {
9260        var _self = this;
9261
9262        this.jq.on('keyup.inputtextarea-maxlength', function(e) {
9263            var value = _self.jq.val(),
9264            length = value.length;
9265
9266            if(length > _self.cfg.maxlength) {
9267                _self.jq.val(value.substr(0, _self.cfg.maxlength));
9268            }
9269        });
9270    },
9271
9272    updateCounter: function() {
9273        var value = this.jq.val(),
9274        length = value.length;
9275
9276        if(this.counter) {
9277            var remaining = this.cfg.maxlength - length;
9278            if(remaining < 0) {
9279                remaining = 0;
9280            }
9281
9282            var counterText = this.cfg.counterTemplate
9283                    .replace('{0}', remaining)
9284                    .replace('{1}', length)
9285                    .replace('{2}', this.cfg.maxlength);
9286
9287            this.counter.text(counterText);
9288        }
9289    },
9290
9291    setupAutoComplete: function() {
9292        var panelMarkup = '<div id="' + this.id + '_panel" class="ui-autocomplete-panel ui-widget-content ui-corner-all ui-helper-hidden ui-shadow"></div>',
9293        _self = this;
9294
9295        this.panel = $(panelMarkup).appendTo(document.body);
9296
9297        this.jq.keyup(function(e) {
9298            var keyCode = $.ui.keyCode;
9299
9300            switch(e.which) {
9301
9302                case keyCode.UP:
9303                case keyCode.LEFT:
9304                case keyCode.DOWN:
9305                case keyCode.RIGHT:
9306                case keyCode.ENTER:
9307                case keyCode.TAB:
9308                case keyCode.SPACE:
9309                case 17: //keyCode.CONTROL:
9310                case 18: //keyCode.ALT:
9311                case keyCode.ESCAPE:
9312                case 224:   //mac command
9313                    //do not search
9314                break;
9315
9316                default:
9317                    var query = _self.extractQuery();
9318                    if(query && query.length >= _self.cfg.minQueryLength) {
9319
9320                         //Cancel the search request if user types within the timeout
9321                        if(_self.timeout) {
9322                            _self.clearTimeout(_self.timeout);
9323                        }
9324
9325                        _self.timeout = setTimeout(function() {
9326                            _self.search(query);
9327                        }, _self.cfg.queryDelay);
9328
9329                    }
9330                break;
9331            }
9332
9333        }).keydown(function(e) {
9334            var overlayVisible = _self.panel.is(':visible'),
9335            keyCode = $.ui.keyCode;
9336
9337            switch(e.which) {
9338                case keyCode.UP:
9339                case keyCode.LEFT:
9340                    if(overlayVisible) {
9341                        var highlightedItem = _self.items.filter('.ui-state-highlight'),
9342                        prev = highlightedItem.length == 0 ? _self.items.eq(0) : highlightedItem.prev();
9343
9344                        if(prev.length == 1) {
9345                            highlightedItem.removeClass('ui-state-highlight');
9346                            prev.addClass('ui-state-highlight');
9347
9348                            if(_self.cfg.scrollHeight) {
9349                                PrimeFaces.scrollInView(_self.panel, prev);
9350                            }
9351                        }
9352
9353                        e.preventDefault();
9354                    }
9355                    else {
9356                        _self.clearTimeout();
9357                    }
9358                break;
9359
9360                case keyCode.DOWN:
9361                case keyCode.RIGHT:
9362                    if(overlayVisible) {
9363                        var highlightedItem = _self.items.filter('.ui-state-highlight'),
9364                        next = highlightedItem.length == 0 ? _self.items.eq(0) : highlightedItem.next();
9365
9366                        if(next.length == 1) {
9367                            highlightedItem.removeClass('ui-state-highlight');
9368                            next.addClass('ui-state-highlight');
9369
9370                            if(_self.cfg.scrollHeight) {
9371                                PrimeFaces.scrollInView(_self.panel, next);
9372                            }
9373                        }
9374
9375                        e.preventDefault();
9376                    }
9377                    else {
9378                        _self.clearTimeout();
9379                    }
9380                break;
9381
9382                case keyCode.ENTER:
9383                    if(overlayVisible) {
9384                        _self.items.filter('.ui-state-highlight').trigger('click');
9385
9386                        e.preventDefault();
9387                    }
9388                    else {
9389                        _self.clearTimeout();
9390                    }
9391                break;
9392
9393                case keyCode.SPACE:
9394                case 17: //keyCode.CONTROL:
9395                case 18: //keyCode.ALT:
9396                case keyCode.BACKSPACE:
9397                case keyCode.ESCAPE:
9398                case 224:   //mac command
9399                    _self.clearTimeout();
9400
9401                    if(overlayVisible) {
9402                        _self.hide();
9403                    }
9404                break;
9405
9406                case keyCode.TAB:
9407                    _self.clearTimeout();
9408
9409                    if(overlayVisible) {
9410                        _self.items.filter('.ui-state-highlight').trigger('click');
9411                        _self.hide();
9412                    }
9413                break;
9414            }
9415        });
9416
9417        //hide panel when outside is clicked
9418        $(document.body).on('mousedown.ui-inputtextarea', function (e) {
9419            if(_self.panel.is(":hidden")) {
9420                return;
9421            }
9422            var offset = _self.panel.offset();
9423            if(e.target === _self.jq.get(0)) {
9424                return;
9425            }
9426
9427            if (e.pageX < offset.left ||
9428                e.pageX > offset.left + _self.panel.width() ||
9429                e.pageY < offset.top ||
9430                e.pageY > offset.top + _self.panel.height()) {
9431                _self.hide();
9432            }
9433        });
9434
9435        //Hide overlay on resize
9436        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', _self.panel, function() {
9437            _self.hide();
9438        });
9439
9440        //dialog support
9441        this.setupDialogSupport();
9442    },
9443
9444    bindDynamicEvents: function() {
9445        var _self = this;
9446
9447        //visuals and click handler for items
9448        this.items.on('mouseover', function() {
9449            var item = $(this);
9450
9451            if(!item.hasClass('ui-state-highlight')) {
9452                _self.items.filter('.ui-state-highlight').removeClass('ui-state-highlight');
9453                item.addClass('ui-state-highlight');
9454            }
9455        })
9456        .on('click', function(event) {
9457            var item = $(this),
9458            itemValue = item.attr('data-item-value'),
9459            selectionStart = _self.jq.getSelection().start,
9460            queryLength = _self.query.length;
9461
9462            _self.jq.focus();
9463
9464            _self.jq.setSelection(selectionStart-queryLength, selectionStart);
9465            _self.jq.replaceSelectedText(itemValue);
9466
9467            _self.invokeItemSelectBehavior(event, itemValue);
9468
9469            _self.hide();
9470        });
9471    },
9472
9473    invokeItemSelectBehavior: function(event, itemValue) {
9474        if(this.hasBehavior('itemSelect')) {
9475            var ext = {
9476                params : [
9477                    {name: this.id + '_itemSelect', value: itemValue}
9478                ]
9479            };
9480
9481            this.callBehavior('itemSelect', ext);
9482        }
9483    },
9484
9485    clearTimeout: function() {
9486        if(this.timeout) {
9487            clearTimeout(this.timeout);
9488        }
9489
9490        this.timeout = null;
9491    },
9492
9493    extractQuery: function() {
9494        var end = this.jq.getSelection().end,
9495        result = /\S+$/.exec(this.jq.get(0).value.slice(0, end)),
9496        lastWord = result ? result[0] : null;
9497
9498        return lastWord;
9499    },
9500
9501    search: function(query) {
9502        this.query = query;
9503
9504        var $this = this,
9505        options = {
9506            source: this.id,
9507            update: this.id,
9508            process: this.id,
9509            params: [
9510                {name: this.id + '_query', value: query}
9511            ],
9512            onsuccess: function(responseXML, status, xhr) {
9513                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
9514                        widget: $this,
9515                        handle: function(content) {
9516                            this.panel.html(content);
9517                            this.items = $th
9517is.panel.find('.ui-autocomplete-item');
9518
9519                            this.bindDynamicEvents();
9520
9521                            if(this.items.length > 0) {
9522                                //highlight first item
9523                                this.items.eq(0).addClass('ui-state-highlight');
9524
9525                                //adjust height
9526                                if(this.cfg.scrollHeight && this.panel.height() > this.cfg.scrollHeight) {
9527                                    this.panel.height(this.cfg.scrollHeight);
9528                                }
9529
9530                                if(this.panel.is(':hidden')) {
9531                                    this.show();
9532                                }  else {
9533                                    this.alignPanel(); //with new items
9534                                }
9535
9536                            }
9537                            else {
9538                                this.panel.hide();
9539                            }
9540                        }
9541                    });
9542
9543                return true;
9544            }
9545        };
9546
9547        if (this.hasBehavior('query')) {
9548            this.callBehavior('query', options);
9549        }
9550        else {
9551            PrimeFaces.ajax.Request.handle(options);
9552        }
9553    },
9554
9555    alignPanel: function() {
9556        var pos = this.jq.getCaretPosition(),
9557        posLeft = (pos.left > 0 ? '+' : '-') + pos.left,
9558        posTop = (pos.top > 0 ? '+' : '-') + pos.top;
9559
9560        this.panel.css({left:'', top:''}).position({
9561            my: 'left top'
9562            ,at: 'left' + posLeft +  ' top' + posTop
9563            ,of: this.jq
9564        });
9565    },
9566
9567    show: function() {
9568        this.panel.css({
9569            'z-index': ++PrimeFaces.zindex,
9570            'width': this.jq.innerWidth(),
9571            'visibility': 'hidden'
9572        }).show();
9573
9574        this.alignPanel();
9575
9576        this.panel.css('visibility', '');
9577    },
9578
9579    hide: function() {
9580        this.panel.hide();
9581    },
9582
9583    setupDialogSupport: function() {
9584        var dialog = this.jq.parents('.ui-dialog:first');
9585
9586        if(dialog.length == 1 && dialog.css('position') === 'fixed') {
9587            this.panel.css('position', 'fixed');
9588        }
9589    }
9590
9591});
9592
9593/**
9594 * PrimeFaces SelectOneMenu Widget
9595 */
9596PrimeFaces.widget.SelectOneMenu = PrimeFaces.widget.DeferredWidget.extend({
9597
9598    init: function(cfg) {
9599        this._super(cfg);
9600
9601        this.panelId = this.jqId + '_panel';
9602        this.input = $(this.jqId + '_input');
9603        this.focusInput = $(this.jqId + '_focus');
9604        this.label = this.jq.find('.ui-selectonemenu-label');
9605        this.menuIcon = this.jq.children('.ui-selectonemenu-trigger');
9606
9607        this.panel = $(this.panelId);
9608        this.disabled = this.jq.hasClass('ui-state-disabled');
9609        this.itemsWrapper = this.panel.children('.ui-selectonemenu-items-wrapper');
9610        this.options = this.input.children('option');
9611        this.cfg.effect = this.cfg.effect||'fade';
9612
9613        this.cfg.effectSpeed = this.cfg.effectSpeed||'normal';
9614        this.cfg.autoWidth = this.cfg.autoWidth === false ? false : true;
9615        this.cfg.dynamic = this.cfg.dynamic === true ? true : false;
9616        this.cfg.appendTo = this.getAppendTo();
9617        this.isDynamicLoaded = false;
9618
9619        if(this.cfg.dynamic) {
9620            var selectedOption = this.options.filter(':selected'),
9621            labelVal = this.cfg.editable ? this.label.val() : selectedOption.text();
9622
9623            this.setLabel(labelVal);
9624        }
9625        else {
9626            this.initContents();
9627            this.bindItemEvents();
9628        }
9629
9630        //triggers
9631        this.triggers = this.cfg.editable ? this.jq.find('.ui-selectonemenu-trigger') : this.jq.find('.ui-selectonemenu-trigger, .ui-selectonemenu-label');
9632
9633        //mark trigger and descandants of trigger as a trigger for a primefaces overlay
9634        this.triggers.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
9635
9636        if(!this.disabled) {
9637            this.bindEvents();
9638            this.bindConstantEvents();
9639
9640            PrimeFaces.utils.registerDynamicOverlay(this, this.panel, this.id + '_panel');
9641        }
9642
9643        // see #7602
9644        if (PrimeFaces.env.touch) {
9645            this.focusInput.attr('readonly', true);
9646        }
9647
9648        this.renderDeferred();
9649    },
9650
9651    initContents: function() {
9652        this.itemsContainer = this.itemsWrapper.children('.ui-selectonemenu-items');
9653        this.items = this.itemsContainer.find('.ui-selectonemenu-item');
9654        this.optGroupsSize = this.itemsContainer.children('li.ui-selectonemenu-item-group').length;
9655
9656        var $this = this,
9657        selectedOption = this.options.filter(':selected'),
9658        highlightedItem = this.items.eq(selectedOption.index());
9659
9660        //disable options
9661        this.options.filter(':disabled').each(function() {
9662            $this.items.eq($(this).index()).addClass('ui-state-disabled');
9663        });
9664
9665        //activate selected
9666        if(this.cfg.editable) {
9667            var customInputVal = this.label.val();
9668
9669            //predefined input
9670            if(customInputVal === selectedOption.text()) {
9671                this.highlightItem(highlightedItem);
9672            }
9673            //custom input
9674            else {
9675                this.items.eq(0).addClass('ui-state-highlight');
9676                this.customInput = true;
9677                this.customInputVal = customInputVal;
9678            }
9679        }
9680        else {
9681            this.highlightItem(highlightedItem);
9682        }
9683
9684        if(this.cfg.syncTooltip) {
9685            this.syncTitle(selectedOption);
9686        }
9687
9688        //pfs metadata
9689        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
9690
9691        //for Screen Readers
9692        for(var i = 0; i < this.items.length; i++) {
9693            this.items.eq(i).attr('id', this.id + '_' + i);
9694        }
9695
9696        var highlightedItemId = highlightedItem.attr('id');
9697        this.jq.attr('aria-owns', this.itemsContainer.attr('id'));
9698        this.focusInput.attr('aria-autocomplete', 'list')
9699            .attr('aria-activedescendant', highlightedItemId)
9700            .attr('aria-describedby', highlightedItemId)
9701            .attr('aria-disabled', this.disabled);
9702        this.itemsContainer.attr('aria-activedescendant', highlightedItemId);
9703    },
9704
9705    _render: function() {
9706        var contentStyle = this.jq.attr('style'),
9707        hasWidth = contentStyle && contentStyle.indexOf('width') != -1;
9708
9709        if(this.cfg.autoWidth && !hasWidth) {
9710            this.jq.css('min-width', this.input.outerWidth());
9711        }
9712    },
9713
9714    //@override
9715    refresh: function(cfg) {
9716        this.panelWidthAdjusted = false;
9717
9718        this._super(cfg);
9719    },
9720
9721    alignPanelWidth: function() {
9722        //align panel and container
9723        if(!this.panelWidthAdjusted) {
9724            var jqWidth = this.jq.outerWidth();
9725            if(this.panel.outerWidth() < jqWidth) {
9726                this.panel.width(jqWidth);
9727            }
9728            else {
9729                this.panel.width(this.panel.width());
9730            }
9731
9732            this.panelWidthAdjusted = true;
9733        }
9734    },
9735
9736    bindEvents: function() {
9737        var $this = this;
9738
9739        // Screen Reader(JAWS) hack on Chrome
9740        if(PrimeFaces.env.browser.webkit) {
9741            this.input.on('focus', function(){
9742                setTimeout(function(){
9743                    $this.focusInput.trigger('focus.ui-selectonemenu');
9744                },2);
9745            });
9746        }
9747
9748        //Triggers
9749        this.triggers.mouseenter(function() {
9750            if(!$this.jq.hasClass('ui-state-focus')) {
9751                $this.jq.addClass('ui-state-hover');
9752                $this.menuIcon.addClass('ui-state-hover');
9753            }
9754        })
9755        .mouseleave(function() {
9756            $this.jq.removeClass('ui-state-hover');
9757            $this.menuIcon.removeClass('ui-state-hover');
9758        })
9759        .click(function(e) {
9760            if($this.panel.is(":hidden")) {
9761                $this.show();
9762            }
9763            else {
9764                $this.hide();
9765
9766                $this.revert();
9767                $this.changeAriaValue($this.getActiveItem());
9768            }
9769
9770            $this.jq.removeClass('ui-state-hover');
9771            $this.menuIcon.removeClass('ui-state-hover');
9772            $this.focusInput.trigger('focus.ui-selectonemenu');
9773            e.preventDefault();
9774        });
9775
9776        this.focusInput.on('focus.ui-selectonemenu', function() {
9777            $this.jq.addClass('ui-state-focus');
9778            $this.menuIcon.addClass('ui-state-focus');
9779        })
9780        .on('blur.ui-selectonemenu', function(){
9781            $this.jq.removeClass('ui-state-focus');
9782            $this.menuIcon.removeClass('ui-state-focus');
9783
9784            $this.callBehavior('blur');
9785        });
9786
9787        //onchange handler for editable input
9788        if(this.cfg.editable) {
9789            this.label.change(function(e) {
9790                $this.triggerChange(true);
9791                $this.callHandleMethod($this.handleLabelChange, e);
9792            });
9793        }
9794
9795        //key bindings
9796        this.bindKeyEvents();
9797
9798        //filter
9799        if(this.cfg.filter) {
9800            this.cfg.initialHeight = this.itemsWrapper.height();
9801            this.setupFilterMatcher();
9802            this.filterInput = this.panel.find('> div.ui-selectonemenu-filter-container > input.ui-selectonemenu-filter');
9803            PrimeFaces.skinInput(this.filterInput);
9804
9805            this.bindFilterEvents();
9806        }
9807    },
9808
9809    bindItemEvents: function() {
9810        var $this = this;
9811
9812        //Items
9813        this.items.filter(':not(.ui-state-disabled)').on('mouseover.selectonemenu', function() {
9814            var el = $(this);
9815
9816            if(!el.hasClass('ui-state-highlight'))
9817                $(this).addClass('ui-state-hover');
9818        })
9819        .on('mouseout.selectonemenu', function() {
9820            $(this).removeClass('ui-state-hover');
9821        })
9822        .on('click.selectonemenu', function() {
9823            $this.revert();
9824            $this.selectItem($(this));
9825            $this.changeAriaValue($(this));
9826        });
9827    },
9828
9829    bindConstantEvents: function() {
9830        var $this = this;
9831
9832        PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.panel,
9833            function() { return  $this.label.add($this.menuIcon); },
9834            function(e, eventTarget) {
9835                if(!($this.panel.is(eventTarget) || $this.panel.has(eventTarget).length > 0)) {
9836                    $this.hide();
9837                    setTimeout(function() {
9838                        $this.revert();
9839                        $this.changeAriaValue($this.getActiveItem());
9840                    }, 2);
9841                }
9842            });
9843
9844        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.panel, function() {
9845            $this.alignPanel();
9846        });
9847    },
9848
9849    unbindEvents: function() {
9850        this.items.off();
9851        this.triggers.off();
9852        this.input.off();
9853        this.focusInput.off();
9854        this.label.off();
9855    },
9856
9857    revert: function() {
9858        if(this.cfg.editable && this.customInput) {
9859            this.setLabel(this.customInputVal);
9860            this.items.filter('.ui-state-active').removeClass('ui-state-active');
9861            this.items.eq(0).addClass('ui-state-active');
9862        }
9863        else {
9864            this.highlightItem(this.items.eq(this.preShowValue.index()));
9865        }
9866    },
9867
9868    highlightItem: function(item) {
9869        this.items.attr('aria-selected', false);
9870        this.items.filter('.ui-state-highlight').removeClass('ui-state-highlight');
9871
9872        if(item.length > 0) {
9873            item.addClass('ui-state-highlight');
9874            item.attr('aria-selected', true);
9875            this.setLabel(item.data('label'));
9876        }
9877    },
9878
9879    triggerChange: function(edited) {
9880        this.changed = false;
9881
9882        this.input.trigger('change');
9883
9884        if(!edited) {
9885            this.value = this.options.filter(':selected').val();
9886        }
9887    },
9888
9889    /**
9890     * Handler to process item selection with mouse
9891     */
9892    selectItem: function(item, silent) {
9893        var selectedOption = this.options.eq(this.resolveItemIndex(item)),
9894        currentOption = this.options.filter(':selected'),
9895        sameOption = selectedOption.val() == currentOption.val(),
9896        shouldChange = null;
9897
9898        if(this.cfg.editable) {
9899            shouldChange = (!sameOption)||(selectedOption.text() != this.label.val());
9900        }
9901        else {
9902            shouldChange = !sameOption;
9903        }
9904
9905        if(shouldChange) {
9906            this.highlightItem(item);
9907            this.input.val(selectedOption.val())
9908
9909            this.triggerChange();
9910
9911            if(this.cfg.editable) {
9912                this.customInput = false;
9913            }
9914
9915            if(this.cfg.syncTooltip) {
9916                this.syncTitle(selectedOption);
9917            }
9918        }
9919
9920        if(!silent) {
9921            this.callBehavior('itemSelect');
9922            this.focusInput.trigger('focus');
9923        }
9924
9925        if(this.panel.is(':visible')) {
9926            this.hide();
9927        }
9928    },
9929
9930    syncTitle: function(option) {
9931        var optionTitle = this.items.eq(option.index()).attr('title');
9932        if(optionTitle)
9933            this.jq.attr('title', this.items.eq(option.index()).attr('title'));
9934        else
9935            this.jq.removeAttr('title');
9936    },
9937
9938    resolveItemIndex: function(item) {
9939        if(this.optGroupsSize === 0)
9940            return item.index();
9941        else
9942            return item.index() - item.prevAll('li.ui-selectonemenu-item-group').length;
9943    },
9944
9945    bindKeyEvents: function() {
9946        var $this = this;
9947
9948        this.focusInput.on('keydown.ui-selectonemenu', function(e) {
9949            var keyCode = $.ui.keyCode,
9950            key = e.which;
9951
9952            switch(key) {
9953                case keyCode.UP:
9954                case keyCode.LEFT:
9955                    $this.callHandleMethod($this.highlightPrev, e);
9956                break;
9957
9958                case keyCode.DOWN:
9959                case keyCode.RIGHT:
9960                    $this.callHandleMethod($this.highlightNext, e);
9961                break;
9962
9963                case keyCode.ENTER:
9964                    $this.handleEnterKey(e);
9965                break;
9966
9967                case keyCode.TAB:
9968                    $this.handleTabKey();
9969                break;
9970
9971                case keyCode.ESCAPE:
9972                    $this.handleEscapeKey(e);
9973                break;
9974
9975                case keyCode.SPACE:
9976                    $this.handleSpaceKey(e);
9977                break;
9978            }
9979        })
9980        .on('keyup.ui-selectonemenu', function(e) {
9981            var keyCode = $.ui.keyCode,
9982            key = e.which;
9983
9984            switch(key) {
9985                case keyCode.UP:
9986                case keyCode.LEFT:
9987                case keyCode.DOWN:
9988                case keyCode.RIGHT:
9989                case keyCode.ENTER:
9990                case keyCode.TAB:
9991                case keyCode.ESCAPE:
9992                case keyCode.SPACE:
9993                case keyCode.HOME:
9994                case keyCode.PAGE_DOWN:
9995                case keyCode.PAGE_UP:
9996                case keyCode.END:
9997                case keyCode.DELETE:
9998                case 16: //shift
9999                case 17: //keyCode.CONTROL:
10000                case 18: //keyCode.ALT:
10001                case 19: //Pause/Break:
10002                case 20: //capslock:
10003                case 44: //Print Screen:
10004                case 45: //Insert:
10005                case 91: //left window or cmd:
10006                case 92: //right window:
10007                case 93: //right cmd:
10008                case 144: //num lock:
10009                case 145: //scroll lock:
10010                break;
10011
10012                default:
10013                    //function keys (F1,F2 etc.)
10014                    if(key >= 112 && key <= 123) {
10015                        break;
10016                    }
10017
10018
10019                    var matchedOptions = null,
10020                    metaKey = e.metaKey||e.ctrlKey||e.shiftKey;
10021
10022                    if(!metaKey) {
10023                        clearTimeout($this.searchTimer);
10024
10025                        // #4682: check for word match
10026                        var text = $(this).val();
10027                        matchedOptions = $this.matchOptions(text);
10028                        if(matchedOptions.length) {
10029                            var highlightItem = $this.items.eq(matchedOptions.index());
10030                            if($this.panel.is(':hidden')) {
10031                                $this.selectItem(highlightItem);
10032                            }
10033                            else {
10034                                $this.highlightItem(highlightItem);
10035                                PrimeFaces.scrollInView($this.itemsWrapper, highlightItem);
10036                            }
10037                        } else {
10038                            // #4682: check for first letter match
10039                            text = String.fromCharCode(key).toLowerCase();
10040                            // find all options with the same first letter
10041                            matchedOptions = $this.matchOptions(text);
10042                            if(matchedOptions.length) {
10043                                var selectedIndex = -1;
10044
10045                                // is current selection one of our matches?
10046                                matchedOptions.each(function() {
10047                                   var option = $(this);
10048                                   var currentIndex = option.index();
10049                                   var currentItem = $this.items.eq(currentIndex);
10050                                   if (currentItem.hasClass('ui-state-highlight')) {
10051                                       selectedIndex = currentIndex;
10052                                       return false;
10053                                   }
10054                                });
10055
10056                                matchedOptions.each(function() {
10057                                    var option = $(this);
10058                                    var currentIndex = option.index();
10059                                    var currentItem = $this.items.eq(currentIndex);
10060
10061                                    // select next item after the current selection
10062                                    if (currentIndex > selectedIndex) {
10063                                         if($this.panel.is(':hidden')) {
10064                                             $this.selectItem(currentItem);
10065                                         }
10066                                         else {
10067                                             $this.highlightItem(currentItem);
10068                                             PrimeFaces.scrollInView($this.itemsWrapper, currentItem);
10069                                         }
10070                                         return false;
10071                                     }
10072                                });
10073                            }
10074                        }
10075
10076                        $this.searchTimer = setTimeout(function(){
10077                            $this.focusInput.val('');
10078                        }, 1000);
10079                    }
10080                break;
10081            }
10082        });
10083    },
10084
10085    matchOptions: function(text) {
10086        return this.options.filter(function() {
10087            var option = $(this);
10088            return (option.is(':not(:disabled)') && (option.text().toLowerCase().indexOf(text) === 0));
10089        });
10090    },
10091
10092    bindFilterEvents: function() {
10093        var $this = this;
10094
10095        this.filterInput.on('keyup.ui-selectonemenu', function(e) {
10096            var keyCode = $.ui.keyCode,
10097            key = e.which;
10098
10099            switch(key) {
10100                case keyCode.UP:
10101                case keyCode.LEFT:
10102                case keyCode.DOWN:
10103                case keyCode.RIGHT:
10104                case keyCode.ENTER:
10105                case keyCode.TAB:
10106                case keyCode.ESCAPE:
10107                case keyCode.SPACE:
10108                case keyCode.HOME:
10109                case keyCode.PAGE_DOWN:
10110                case keyCode.PAGE_UP:
10111                case keyCode.END:
10112                case 16: //shift
10113                case 17: //keyCode.CONTROL:
10114                case 18: //keyCode.ALT:
10115                case 91: //left window or cmd:
10116                case 92: //right window:
10117                case 93: //right cmd:
10118                case 20: //capslock:
10119                break;
10120
10121                default:
10122                    //function keys (F1,F2 etc.)
10123                    if(key >= 112 && key <= 123) {
10124                        break;
10125                    }
10126
10127                    var metaKey = e.metaKey||e.ctrlKey;
10128
10129                    if(!metaKey) {
10130                        $this.filter($(this).val());
10131                    }
10132                break;
10133            }
10134        })
10135        .on('keydown.ui-selectonemenu',function(e) {
10136            var keyCode = $.ui.keyCode,
10137            key = e.which;
10138
10139            switch(key) {
10140                case keyCode.UP:
10141                    $this.highlightPrev(e);
10142                break;
10143
10144                case keyCode.DOWN:
10145                    $this.highlightNext(e);
10146                break;
10147
10148                case keyCode.ENTER:
10149                    $this.handleEnterKey(e);
10150                    e.stopPropagation();
10151                break;
10152
10153                case keyCode.TAB:
10154                    $this.handleTabKey();
10155                break;
10156
10157                case keyCode.ESCAPE:
10158                    $this.handleEscapeKey(e);
10159                break;
10160
10161                case keyCode.SPACE:
10162                    $this.handleSpaceKey(e);
10163                break;
10164
10165                default:
10166                break;
10167            }
10168        }).on('paste.ui-selectonemenu', function() {
10169            setTimeout(function(){
10170                $this.filter($this.filterInput.val());
10171            },2);
10172		});
10173    },
10174
10175    highlightNext: function(event) {
10176        var activeItem = this.getActiveItem(),
10177        next = this.panel.is(':hidden') ? activeItem.nextAll(':not(.ui-state-disabled,.ui-selectonemenu-item-group):first')
10178                                : activeItem.nextAll(':not(.ui-state-disabled,.ui-selectonemenu-item-group):visible:first');
10179
10180        if(event.altKey) {
10181            this.show();
10182        }
10183        else {
10184            if(next.length === 1) {
10185                if(this.panel.is(':hidden')) {
10186                    this.selectItem(next);
10187                }
10188                else {
10189                    this.highlightItem(next);
10190                    PrimeFaces.scrollInView(this.itemsWrapper, next);
10191                }
10192                this.changeAriaValue(next);
10193            }
10194        }
10195
10196        event.preventDefault();
10197    },
10198
10199    highlightPrev: function(event) {
10200        var activeItem = this.getActiveItem(),
10201        prev = this.panel.is(':hidden') ? activeItem.prevAll(':not(.ui-state-disabled,.ui-selectonemenu-item-group):first')
10202                                : activeItem.prevAll(':not(.ui-state-disabled,.ui-selectonemenu-item-group):visible:first');
10203
10204        if(prev.length === 1) {
10205            if(this.panel.is(':hidden')) {
10206                this.selectItem(prev);
10207            }
10208            else {
10209                this.highlightItem(prev);
10210                PrimeFaces.scrollInView(this.itemsWrapper, prev);
10211            }
10212            this.changeAriaValue(prev);
10213        }
10214
10215        event.preventDefault();
10216    },
10217
10218    handleEnterKey: function(event) {
10219        if(this.panel.is(':visible')) {
10220            this.selectItem(this.getActiveItem());
10221        }
10222
10223        event.preventDefault();
10224    },
10225
10226    handleSpaceKey: function(event) {
10227        var target = $(event.target);
10228
10229        if(target.is('input') && target.hasClass('ui-selectonemenu-filter')) {
10230            return;
10231        }
10232
10233        if(this.panel.is(":hidden")) {
10234            this.show();
10235        }
10236        else {
10237            this.hide();
10238
10239            this.revert();
10240            this.changeAriaValue(this.getActiveItem());
10241        }
10242
10243        event.preventDefault();
10244    },
10245
10246    handleEscapeKey: function(event) {
10247        if(this.panel.is(':visible')) {
10248            this.revert();
10249            this.hide();
10250        }
10251
10252        event.preventDefault();
10253    },
10254
10255    handleTabKey: function() {
10256        if(this.panel.is(':visible')) {
10257            this.selectItem(this.getActiveItem());
10258        }
10259    },
10260
10261    handleLabelChange: function(event) {
10262        this.customInput = true;
10263        this.customInputVal = $(event.target).val();
10264        this.items.filter('.ui-state-active').removeClass('ui-state-active');
10265        this.items.eq(0).addClass('ui-state-active');
10266    },
10267
10268    show: function() {
10269        this.callHandleMethod(this._show, null);
10270    },
10271
10272    _show: function() {
10273        var $this = this;
10274
10275        this.panel.css({'display':'block', 'opacity':0, 'pointer-events': 'none'});
10276        this.itemsWrapper.css({'overflow': 'scroll'});
10277
10278        this.alignPanel();
10279
10280        this.panel.css({'display':'none', 'opacity':'', 'pointer-events': '', 'z-index': ++PrimeFaces.zindex});
10281        this.itemsWrapper.css({'overflow': ''});
10282
10283        if(this.cfg.effect !== 'none') {
10284            this.panel.show(this.cfg.effect, {}, this.cfg.effectSpeed, function() {
10285                PrimeFaces.scrollInView($this.itemsWrapper, $this.getActiveItem());
10286
10287                if($this.cfg.filter)
10288                    $this.focusFilter();
10289            });
10290        }
10291        else {
10292            this.panel.show();
10293            PrimeFaces.scrollInView(this.itemsWrapper, this.getActiveItem());
10294
10295            if($this.cfg.filter)
10296                this.focusFilter(10);
10297        }
10298
10299        //value before panel is shown
10300        this.preShowValue = this.options.filter(':selected');
10301        this.focusInput.attr('aria-expanded', true);
10302        this.jq.attr('aria-expanded', true);
10303    },
10304
10305    hide: function() {
10306        this.panel.css('z-index', '').hide();
10307        this.focusInput.attr('aria-expanded', false);
10308        this.jq.attr('aria-expanded', false);
10309    },
10310
10311    focus: function() {
10312        this.focusInput.focus();
10313    },
10314
10315    focusFilter: function(timeout) {
10316        if(timeout) {
10317            var $this = this;
10318            setTimeout(function() {
10319                $this.focusFilter();
10320            }, timeout);
10321        }
10322        else {
10323            this.filterInput.focus();
10324        }
10325    },
10326
10327    blur: function() {
10328        this.focusInput.blur();
10329
10330        this.callBehavior('blur');
10331    },
10332
10333    disable: function() {
10334    	if (!this.disabled) {
10335	        this.disabled = true;
10336	        this.jq.addClass('ui-state-disabled');
10337	        this.input.attr('disabled', 'disabled');
10338	        if(this.cfg.editable) {
10339	            this.label.attr('disabled', 'disabled');
10340	        }
10341	        this.unbindEvents();
10342    	}
10343    },
10344
10345    enable: function() {
10346    	if (this.disabled) {
10347	        this.disabled = false;
10348	        this.jq.removeClass('ui-state-disabled');
10349	        this.input.removeAttr('disabled');
10350	        if(this.cfg.editable) {
10351	            this.label.removeAttr('disabled');
10352	        }
10353
10354            this.bindEvents();
10355            this.bindItemEvents();
10356    	}
10357    },
10358
10359    alignPanel: function() {
10360        this.alignPanelWidth();
10361
10362        if(this.panel.parent().is(this.jq)) {
10363            this.panel.css({
10364                left: 0,
10365                top: this.jq.innerHeight()
10366            });
10367        }
10368        else {
10369            this.panel.css({left:0, top:0}).position({
10370                my: 'left top'
10371                ,at: 'left bottom'
10372                ,of: this.jq
10373                ,collision: 'flipfit'
10374            });
10375        }
10376    },
10377
10378    setLabel: function(value) {
10379        var displayedLabel = this.getLabelToDisplay(value);
10380
10381        if (this.cfg.editable) {
10382            if (value === '&nbsp;')
10383                this.label.val('');
10384            else
10385                this.label.val(displayedLabel);
10386
10387            var hasPlaceholder = this.label[0].hasAttribute('placeholder');
10388            this.updatePlaceholderClass((hasPlaceholder && value === '&nbsp;'));
10389        }
10390        else {
10391            var labelText = this.label.data('placeholder');
10392            if (labelText == null || labelText == "") {
10393                labelText = '&nbsp;';
10394            }
10395
10396            this.updatePlaceholderClass((value === '&nbsp;' && labelText !== '&nbsp;'));
10397
10398            if (value === '&nbsp;') {
10399                if (labelText != '&nbsp;') {
10400                   this.label.text(labelText);
10401                } else {
10402                    this.label.html(labelText);
10403                }
10404            }
10405            else {
10406                this.label.removeClass('ui-state-disabled');
10407
10408                var option = null;
10409                if(this.items) {
10410                    var selectedItem = this.items.filter('[data-label="' + $.escapeSelector(value) + '"]');
10411                    option = this.options.eq(this.resolveItemIndex(selectedItem));
10412                }
10413                else {
10414                    option = this.options.filter(':selected');
10415                }
10416
10417                if (option && option.data('escape') == false) {
10418                    this.label.html(displayedLabel);
10419                } else {
10420                    this.label.text(displayedLabel);
10421                }
10422            }
10423        }
10424    },
10425
10426    selectValue : function(value) {
10427        var option = this.options.filter('[value="' + $.escapeSelector(value) + '"]');
10428
10429        this.selectItem(this.items.eq(option.index()), true);
10430    },
10431
10432    getActiveItem: function() {
10433        return this.items.filter('.ui-state-highlight');
10434    },
10435
10436    setupFilterMatcher: function() {
10437        this.cfg.filterMatchMode = this.cfg.filterMatchMode||'startsWith';
10438        this.filterMatchers = {
10439            'startsWith': this.startsWithFilter
10440            ,'contains': this.containsFilter
10441            ,'endsWith': this.endsWithFilter
10442            ,'custom': this.cfg.filterFunction
10443        };
10444
10445        this.filterMatcher = this.filterMatchers[this.cfg.filterMatchMode];
10446    },
10447
10448    startsWithFilter: function(value, filter) {
10449        return value.indexOf(filter) === 0;
10450    },
10451
10452    containsFilter: function(value, filter) {
10453        return value.indexOf(filter) !== -1;
10454    },
10455
10456    endsWithFilter: function(value, filter) {
10457        return value.indexOf(filter, value.length - filter.length) !== -1;
10458    },
10459
10460    filter: function(value) {
10461        this.cfg.initialHeight = this.cfg.initialHeight||this.itemsWrapper.height();
10462        var filterValue = this.cfg.caseSensitive ? $.trim(value) : $.trim(value).toLowerCase();
10463
10464        if(filterValue === '') {
10465            this.items.filter(':hidden').show();
10466            this.itemsContainer.children('.ui-selectonemenu-item-group').show();
10467        }
10468        else {
10469            var hide = [];
10470            var show = [];
10471
10472            for(var i = 0; i < this.options.length; i++) {
10473                var option = this.options.eq(i),
10474                itemLabel = this.cfg.caseSensitive ? option.text() : option.text().toLowerCase(),
10475                item = this.items.eq(i);
10476
10477                if(item.hasClass('ui-noselection-option')) {
10478                    hide.push(item);
10479                }
10480                else {
10481                    if(this.filterMatcher(itemLabel, filterValue))
10482                        show.push(item);
10483                    else
10484                        hide.push(item);
10485                }
10486            }
10487
10488            $.each(hide, function(i, o) { o.hide() });
10489            $.each(show, function(i, o) { o.show() });
10490            hide = [];
10491            show = [];
10492
10493            //Toggle groups
10494            var groups = this.itemsContainer.children('.ui-selectonemenu-item-group');
10495            for(var g = 0; g < groups.length; g++) {
10496                var group = groups.eq(g);
10497
10498                if(g === (groups.length - 1)) {
10499                    if(group.nextAll().filter(':visible').length === 0)
10500                        hide.push(group);
10501                    else
10502                        show.push(group);
10503                }
10504                else {
10505                    if(group.nextUntil('.ui-selectonemenu-item-group').filter(':visible').length === 0)
10506                        hide.push(group);
10507                    else
10508                        show.push(group);
10509                }
10510            }
10511
10512            $.each(hide, function(i, o) { o.hide() });
10513            $.each(show, function(i, o) { o.show() });
10514        }
10515
10516        var firstVisibleItem = this.items.filter(':visible:not(.ui-state-disabled):first');
10517        if(firstVisibleItem.length) {
10518            this.highlightItem(firstVisibleItem);
10519        }
10520
10521        if(this.itemsContainer.height() < this.cfg.initialHeight) {
10522            this.itemsWrapper.css('height', 'auto');
10523        }
10524        else {
10525            this.itemsWrapper.height(this.cfg.initialHeight);
10526        }
10527
10528        this.alignPanel();
10529    },
10530
10531    getSelectedValue: function() {
10532        return this.input.val();
10533    },
10534
10535    getSelectedLabel: function() {
10536        return this.options.filter(':selected').text();
10537    },
10538
10539    getLabelToDisplay: function(value) {
10540        if(this.cfg.labelTemplate && value !== '&nbsp;') {
10541            return this.cfg.labelTemplate.replace('{0}', value);
10542        }
10543        return String(value);
10544    },
10545
10546    changeAriaValue: function (item) {
10547        var itemId = item.attr('id');
10548
10549        this.focusInput.attr('aria-activedescendant', itemId)
10550                .attr('aria-describedby', itemId);
10551        this.itemsContainer.attr('aria-activedescendant', itemId);
10552    },
10553
10554    dynamicPanelLoad: function() {
10555        var $this = this,
10556        options = {
10557            source: this.id,
10558            process: this.id,
10559            update: this.id,
10560            global: false,
10561            params: [{name: this.id + '_dynamicload', value: true}],
10562            onsuccess: function(responseXML, status, xhr) {
10563                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
10564                    widget: $this,
10565                    handle: function(content) {
10566                        var $content = $($.parseHTML(content));
10567
10568                        var $ul = $content.filter('ul');
10569                        $this.itemsWrapper.empty();
10570                        $this.itemsWrapper.append($ul);
10571
10572                        var $select = $content.filter('select');
10573                        $this.input.replaceWith($select);
10574                    }
10575                });
10576
10577                return true;
10578            },
10579            oncomplete: function(xhr, status, args, data) {
10580                $this.isDynamicLoaded = true;
10581                $this.input = $($this.jqId + '_input');
10582                $this.options = $this.input.children('option');
10583                $this.initContents();
10584                $this.bindItemEvents();
10585            }
10586        };
10587
10588        PrimeFaces.ajax.Request.handle(options);
10589    },
10590
10591    callHandleMethod: function(handleMethod, event) {
10592        var $this = this;
10593        if(this.cfg.dynamic && !this.isDynamicLoaded) {
10594            this.dynamicPanelLoad();
10595
10596            var interval = setInterval(function() {
10597                if($this.isDynamicLoaded) {
10598                    handleMethod.call($this, event);
10599
10600                    clearInterval(interval);
10601                }
10602            }, 10);
10603        }
10604        else {
10605            handleMethod.call(this, event);
10606        }
10607    },
10608
10609    getAppendTo: function() {
10610        var dialog = this.jq[0].closest('.ui-dialog');
10611        if (dialog) {
10612            var $dialog = $(dialog);
10613            //set position as fixed to scroll with dialog
10614            if ($dialog.css('position') === 'fixed') {
10615                this.panel.css('position', 'fixed');
10616            }
10617
10618            //append to body if not already appended by user choice
10619            if(!this.panel.parent().is(document.body)) {
10620                return "@(body)";
10621            }
10622        }
10623
10624        return this.cfg.appendTo;
10625    },
10626
10627    updatePlaceholderClass: function(add) {
10628        if (add) {
10629            this.label.addClass('ui-selectonemenu-label-placeholder');
10630        }
10631        else {
10632            this.label.removeClass('ui-selectonemenu-label-placeholder');
10633        }
10634    }
10635
10636});
10637
10638/**
10639 * PrimeFaces SelectOneRadio Widget
10640 */
10641PrimeFaces.widget.SelectOneRadio = PrimeFaces.widget.BaseWidget.extend({
10642
10643    init: function(cfg) {
10644        this._super(cfg);
10645
10646        //custom layout
10647        if(this.cfg.custom) {
10648            this.originalInputs = this.jq.find(':radio');
10649            this.inputs = $('input:radio[name="' + this.id + '"].ui-radio-clone');
10650            this.outputs = this.inputs.parent().next('.ui-radiobutton-box');
10651            this.labels = $();
10652
10653            //labels
10654            for(var i=0; i < this.outputs.length; i++) {
10655                this.labels = this.labels.add('label[for="' + this.outputs.eq(i).parent().attr('id') + '"]');
10656            }
10657
10658            //update radio state
10659            for(var i = 0; i < this.inputs.length; i++) {
10660                var input = this.inputs.eq(i),
10661                itemindex = input.data('itemindex'),
10662                original = this.originalInputs.eq(itemindex);
10663
10664                input.val(original.val());
10665
10666                if(original.is(':checked')) {
10667                    input.prop('checked', true).parent().next().addClass('ui-state-active').children('.ui-radiobutton-icon')
10668                            .addClass('ui-icon-bullet').removeClass('ui-icon-blank');
10669                }
10670
10671                if(original.is(':disabled')) {
10672                    this.disable(i);
10673                }
10674            }
10675
10676            //pfs metadata
10677            this.originalInputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
10678        }
10679        //regular layout
10680        else {
10681            this.outputs = this.jq.find('.ui-radiobutton-box');
10682            this.inputs = this.jq.find(':radio');
10683            this.labels = this.jq.find('label');
10684
10685            //pfs metadata
10686            this.inputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
10687        }
10688
10689        this.enabledInputs = this.inputs.filter(':not(:disabled)');
10690        this.checkedRadio = this.outputs.filter('.ui-state-active');
10691
10692        this.bindEvents();
10693    },
10694
10695    refresh: function(cfg) {
10696        if(this.cfg.custom) {
10697            for(var i = 0; i < this.inputs.length; i++) {
10698                var input = this.inputs.eq(i);
10699
10700                this.enable(i);
10701                input.prop('checked', false).parent().next().removeClass('ui-state-active').children('.ui-radiobutton-icon')
10702                            .removeClass('ui-icon-bullet').addClass('ui-icon-blank');
10703            }
10704        }
10705
10706        this.init(cfg);
10707    },
10708
10709    bindEvents: function() {
10710        var $this = this;
10711
10712        this.outputs.filter(':not(.ui-state-disabled)').on('mouseover.selectOneRadio', function() {
10713            $(this).addClass('ui-state-hover');
10714        })
10715        .on('mouseout.selectOneRadio', function() {
10716            $(this).removeClass('ui-state-hover');
10717        })
10718        .on('click.selectOneRadio', function(e) {
10719            var radio = $(this),
10720            input = radio.prev().children(':radio');
10721
10722            if(!radio.hasClass('ui-state-active')) {
10723                $this.unselect($this.checkedRadio);
10724                $this.select(radio);
10725                $this.fireClickEvent(input, e);
10726                input.trigger('change');
10727            }
10728            else {
10729                if ($this.cfg.unselectable) {
10730                    $this.unselect($this.checkedRadio);
10731                }
10732                $this.fireClickEvent(input, e);
10733            }
10734
10735            // Github issue #4467
10736            e.stopPropagation();
10737            e.preventDefault();
10738        });
10739
10740        this.labels.filter(':not(.ui-state-disabled)').on('click.selectOneRadio', function(e) {
10741            var target = $(PrimeFaces.escapeClientId($(this).attr('for'))),
10742            radio = null;
10743
10744            //checks if target is input or not(custom labels)
10745            if(target.is(':input'))
10746                radio = target.parent().next();
10747            else
10748                radio = target.children('.ui-radiobutton-box'); //custom layout
10749
10750            radio.trigger('click.selectOneRadio');
10751
10752            e.preventDefault();
10753        });
10754
10755        this.enabledInputs.on('focus.selectOneRadio', function() {
10756            var input = $(this),
10757            radio = input.parent().next();
10758
10759            radio.addClass('ui-state-focus');
10760        })
10761        .on('blur.selectOneRadio', function() {
10762            var input = $(this),
10763            radio = input.parent().next();
10764
10765            radio.removeClass('ui-state-focus');
10766        })
10767        .on('keydown.selectOneRadio', function(e) {
10768            var input = $(this),
10769            currentRadio = input.parent().next(),
10770            index = $this.enabledInputs.index(input),
10771            size = $this.enabledInputs.length,
10772            keyCode = $.ui.keyCode,
10773            key = e.which;
10774
10775            switch(key) {
10776                case keyCode.UP:
10777                case keyCode.LEFT:
10778                    var prevRadioInput = (index === 0) ? $this.enabledInputs.eq((size - 1)) : $this.enabledInputs.eq(--index),
10779                    prevRadio = prevRadioInput.parent().next();
10780
10781                    input.blur();
10782                    $this.unselect(currentRadio);
10783                    $this.select(prevRadio);
10784                    prevRadioInput.trigger('focus').trigger('change');
10785                    e.preventDefault();
10786                break;
10787
10788                case keyCode.DOWN:
10789                case keyCode.RIGHT:
10790                    var nextRadioInput = (index === (size - 1)) ? $this.enabledInputs.eq(0) : $this.enabledInputs.eq(++index),
10791                    nextRadio = nextRadioInput.parent().next();
10792
10793                    input.blur();
10794                    $this.unselect(currentRadio);
10795                    $this.select(nextRadio);
10796                    nextRadioInput.trigger('focus').trigger('change');
10797                    e.preventDefault();
10798                break;
10799
10800                case keyCode.SPACE:
10801                    if(!input.prop('checked')) {
10802                        $this.select(currentRadio);
10803                        input.trigger('focus').trigger('change');
10804                    }
10805
10806                    e.preventDefault();
10807                break;
10808            }
10809        });
10810    },
10811
10812    unselect: function(radio) {
10813        var radioInput = radio.prev().children(':radio');
10814        radioInput.prop('checked', false);
10815        radio.removeClass('ui-state-active').children('.ui-radiobutton-icon').removeClass('ui-icon-bullet').addClass('ui-icon-blank');
10816
10817        if (this.cfg.custom) {
10818            var itemindex = radioInput.data('itemindex');
10819            this.originalInputs.eq(itemindex).prop('checked', false);
10820        }
10821    },
10822
10823    select: function(radio) {
10824        var radioInput = radio.prev().children(':radio');
10825        this.checkedRadio = radio;
10826        radio.addClass('ui-state-active').children('.ui-radiobutton-icon').addClass('ui-icon-bullet').removeClass('ui-icon-blank');
10827        radioInput.prop('checked', true);
10828
10829        if (this.cfg.custom) {
10830            var itemindex = radioInput.data('itemindex');
10831            this.originalInputs.eq(itemindex).prop('checked', true);
10832        }
10833    },
10834
10835    unbindEvents: function(input) {
10836        if(input) {
10837            input.off();
10838            input.parent().nextAll('.ui-radiobutton-box').off();
10839            this.labels.filter("label[for='" + input.attr('id') + "']").off();
10840        }
10841        else {
10842            this.inputs.off();
10843            this.labels.off();
10844            this.outputs.off();
10845        }
10846    },
10847
10848    disable: function(index) {
10849        if(index == null) {
10850            this.inputs.attr('disabled', 'disabled');
10851            this.labels.addClass('ui-state-disabled');
10852            this.outputs.addClass('ui-state-disabled');
10853            this.unbindEvents();
10854        }
10855        else {
10856            var input = this.inputs.eq(index),
10857                label = this.labels.filter("label[for='" + input.attr('id') + "']");
10858            input.attr('disabled', 'disabled').parent().nextAll('.ui-radiobutton-box').addClass('ui-state-disabled');
10859            label.addClass('ui-state-disabled');
10860            this.unbindEvents(input);
10861        }
10862
10863    },
10864
10865    enable: function(index) {
10866        if(index == null) {
10867            this.inputs.removeAttr('disabled');
10868            this.labels.removeClass('ui-state-disabled');
10869            this.outputs.removeClass('ui-state-disabled');
10870        }
10871        else {
10872            var input = this.inputs.eq(index),
10873                label = this.labels.filter("label[for='" + input.attr('id') + "']");
10874            input.removeAttr('disabled').parent().nextAll('.ui-radiobutton-box').removeClass('ui-state-disabled');
10875            label.removeClass('ui-state-disabled');
10876        }
10877        this.bindEvents();
10878    },
10879
10880    fireClickEvent: function(input, event) {
10881        var userOnClick = input.prop('onclick');
10882        if (userOnClick) {
10883            userOnClick.call(this, event);
10884        }
10885    }
10886
10887});
10888
10889/**
10890 * PrimeFaces SelectBooleanCheckbox Widget
10891 */
10892PrimeFaces.widget.SelectBooleanCheckbox = PrimeFaces.widget.BaseWidget.extend({
10893
10894    init: function(cfg) {
10895        this._super(cfg);
10896
10897        this.input = $(this.jqId + '_input');
10898        this.box = this.jq.find('.ui-chkbox-box');
10899        this.icon = this.box.children('.ui-chkbox-icon');
10900        this.itemLabel = this.jq.find('.ui-chkbox-label');
10901        this.disabled = this.input.is(':disabled');
10902
10903        var $this = this;
10904
10905        //bind events if not disabled
10906        if(!this.disabled) {
10907            this.box.on('mouseover.selectBooleanCheckbox', function() {
10908                $this.box.addClass('ui-state-hover');
10909            })
10910            .on('mouseout.selectBooleanCheckbox', function() {
10911                $this.box.removeClass('ui-state-hover');
10912            })
10913            .on('click.selectBooleanCheckbox', function() {
10914                $this.input.trigger('click');
10915            });
10916
10917            this.input.on('focus.selectBooleanCheckbox', function() {
10918                $this.box.addClass('ui-state-focus');
10919            })
10920            .on('blur.selectBooleanCheckbox', function() {
10921                $this.box.removeClass('ui-state-focus');
10922            })
10923            .on('change.selectBooleanCheckbox', function(e) {
10924                if($this.isChecked()) {
10925                    $this.input.prop('checked', true).attr('aria-checked', true);
10926                    $this.box.addClass('ui-state-active').children('.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
10927                }
10928                else {
10929                    $this.input.prop('checked', false).attr('aria-checked', false);
10930                    $this.box.removeClass('ui-state-active').children('.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
10931                }
10932            });
10933
10934            //toggle state on label click
10935            this.itemLabel.click(function() {
10936                $this.toggle();
10937                $this.input.trigger('focus');
10938            });
10939        }
10940
10941        //pfs metadata
10942        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
10943    },
10944
10945    toggle: function() {
10946        if(this.isChecked())
10947            this.uncheck();
10948        else
10949            this.check();
10950    },
10951
10952    isChecked: function() {
10953        return this.input.prop('checked');
10954    },
10955
10956    check: function() {
10957        if(!this.isChecked()) {
10958            this.input.prop('checked', true).trigger('change');
10959            this.input.attr('aria-checked', true);
10960            this.box.addClass('ui-state-active').children('.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
10961        }
10962    },
10963
10964    uncheck: function() {
10965        if(this.isChecked()) {
10966            this.input.prop('checked', false).trigger('change');
10967            this.input.attr('aria-checked', false);
10968            this.box.removeClass('ui-state-active').children('.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
10969        }
10970    }
10971
10972});
10973
10974/**
10975 * PrimeFaces SelectManyCheckbox Widget
10976 */
10977PrimeFaces.widget.SelectManyCheckbox = PrimeFaces.widget.BaseWidget.extend({
10978
10979    init: function(cfg) {
10980        this._super(cfg);
10981
10982        if(this.cfg.custom) {
10983            this.originalInputs = this.jq.find(':checkbox');
10984            this.inputs = $('input:checkbox[name="' + this.id + '"].ui-chkbox-clone');
10985            this.outputs = this.inputs.parent().next('.ui-chkbox-box');
10986
10987            //update checkbox state
10988            for(var i = 0; i < this.inputs.length; i++) {
10989                var input = this.inputs.eq(i),
10990                itemindex = input.data('itemindex'),
10991                original = this.originalInputs.eq(itemindex);
10992
10993                input.val(original.val());
10994
10995                if(original.is(':checked')) {
10996                    input.prop('checked', true).parent().next().addClass('ui-state-active').children('.ui-chkbox-icon')
10997                            .addClass('ui-icon-check').removeClass('ui-icon-blank');
10998                }
10999            }
11000        }
11001        else {
11002            this.outputs = this.jq.find('.ui-chkbox-box:not(.ui-state-disabled)');
11003            this.inputs = this.jq.find(':checkbox:not(:disabled)');
11004        }
11005
11006        this.enabledInputs = this.inputs.filter(':not(:disabled)');
11007
11008        this.bindEvents();
11009
11010        //pfs metadata
11011        this.inputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
11012    },
11013
11014    bindEvents: function() {
11015        this.outputs.filter(':not(.ui-state-disabled)').on('mouseover', function() {
11016            $(this).addClass('ui-state-hover');
11017        })
11018        .on('mouseout', function() {
11019            $(this).removeClass('ui-state-hover');
11020        })
11021        .on('click', function() {
11022            var checkbox = $(this),
11023            input = checkbox.prev().children(':checkbox');
11024
11025            input.trigger('click');
11026
11027            if($.browser.msie && parseInt($.browser.version) < 9) {
11028                input.trigger('change');
11029            }
11030        });
11031
11032        //delegate focus-blur-change states
11033        this.enabledInputs.on('focus', function() {
11034            var input = $(this),
11035            checkbox = input.parent().next();
11036
11037            checkbox.addClass('ui-state-focus');
11038        })
11039        .on('blur', function() {
11040            var input = $(this),
11041            checkbox = input.parent().next();
11042
11043            checkbox.removeClass('ui-state-focus');
11044        })
11045        .on('change', function(e) {
11046            var input = $(this),
11047            checkbox = input.parent().next(),
11048            disabled = input.is(':disabled');
11049
11050            if(disabled) {
11051                return;
11052            }
11053
11054            if(input.is(':checked')) {
11055                checkbox.children('.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
11056
11057                checkbox.addClass('ui-state-active');
11058            }
11059            else {
11060                checkbox.removeClass('ui-state-active').children('.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
11061            }
11062        });
11063    }
11064
11065});
11066
11067/**
11068 * PrimeFaces SelectListbox Widget
11069 */
11070PrimeFaces.widget.SelectListbox = PrimeFaces.widget.BaseWidget.extend({
11071
11072    init: function(cfg) {
11073        this._super(cfg);
11074
11075        this.input = $(this.jqId + '_input'),
11076        this.listContainer = this.jq.children('.ui-selectlistbox-listcontainer');
11077        this.listElement = this.listContainer.children('.ui-selectlistbox-list');
11078        this.options = $(this.input).children('option');
11079        this.allItems = this.listElement.find('.ui-selectlistbox-item');
11080        this.items = this.allItems.filter(':not(.ui-state-disabled)');
11081
11082        //scroll to selected
11083        var selected = this.options.filter(':selected:not(:disabled)');
11084        if(selected.length) {
11085            PrimeFaces.scrollInView(this.listContainer, this.items.eq(selected.eq(0).index()));
11086        }
11087
11088        this.bindEvents();
11089
11090        //pfs metadata
11091        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
11092    },
11093
11094    bindEvents: function() {
11095        var $this = this;
11096
11097        //items
11098        this.items.on('mouseover.selectListbox', function() {
11099            var item = $(this);
11100            if(!item.hasClass('ui-state-highlight')) {
11101                item.addClass('ui-state-hover');
11102            }
11103        })
11104        .on('mouseout.selectListbox', function() {
11105            $(this).removeClass('ui-state-hover');
11106        })
11107        .on('dblclick.selectListbox', function(e) {
11108            $this.input.trigger('dblclick');
11109
11110            PrimeFaces.clearSelection();
11111            e.preventDefault();
11112        });
11113
11114        //input
11115        this.input.on('focus.selectListbox', function() {
11116            $this.jq.addClass('ui-state-focus');
11117        }).on('blur.selectListbox', function() {
11118            $this.jq.removeClass('ui-state-focus');
11119        });
11120
11121        if(this.cfg.filter) {
11122            this.filterInput = this.jq.find('> div.ui-selectlistbox-filter-container > input.ui-selectlistbox-filter');
11123            PrimeFaces.skinInput(this.filterInput);
11124            this.filterInput.on('keyup.selectListbox', function(e) {
11125                $this.filter(this.value);
11126            });
11127
11128            this.setupFilterMatcher();
11129        }
11130    },
11131
11132    unselectAll: function() {
11133        this.items.removeClass('ui-state-highlight ui-state-hover');
11134        this.options.filter(':selected').prop('selected', false);
11135    },
11136
11137    selectItem: function(item) {
11138        item.addClass('ui-state-highlight').removeClass('ui-state-hover');
11139        this.options.eq(item.index()).prop('selected', true);
11140    },
11141
11142    unselectItem: function(item) {
11143        item.removeClass('ui-state-highlight');
11144        this.options.eq(item.index()).prop('selected', false);
11145    },
11146
11147    setupFilterMatcher: function() {
11148        this.cfg.filterMatchMode = this.cfg.filterMatchMode||'startsWith';
11149        this.filterMatchers = {
11150            'startsWith': this.startsWithFilter
11151            ,'contains': this.containsFilter
11152            ,'endsWith': this.endsWithFilter
11153            ,'custom': this.cfg.filterFunction
11154        };
11155
11156        this.filterMatcher = this.filterMatchers[this.cfg.filterMatchMode];
11157    },
11158
11159    startsWithFilter: function(value, filter) {
11160        return value.indexOf(filter) === 0;
11161    },
11162
11163    containsFilter: function(value, filter) {
11164        return value.indexOf(filter) !== -1;
11165    },
11166
11167    endsWithFilter: function(value, filter) {
11168        return value.indexOf(filter, value.length - filter.length) !== -1;
11169    },
11170
11171    filter: function(value) {
11172        var filterValue = this.cfg.caseSensitive ? $.trim(value) : $.trim(value).toLowerCase();
11173
11174        if(filterValue === '') {
11175            this.items.filter(':hidden').show();
11176        }
11177        else {
11178            for(var i = 0; i < this.options.length; i++) {
11179                var option = this.options.eq(i),
11180                itemLabel = this.cfg.caseSensitive ? option.text() : option.text().toLowerCase(),
11181                item = this.items.eq(i);
11182
11183                if(this.filterMatcher(itemLabel, filterValue))
11184                    item.show();
11185                else
11186                    item.hide();
11187            }
11188        }
11189    }
11190});
11191
11192/**
11193 * PrimeFaces SelectOneListbox Widget
11194 */
11195PrimeFaces.widget.SelectOneListbox = PrimeFaces.widget.SelectListbox.extend({
11196
11197    bindEvents: function() {
11198        this._super();
11199        var $this = this;
11200
11201        if(!this.cfg.disabled) {
11202            this.focusedItem = null;
11203            
11204            this.items.on('click.selectListbox', function(e) {
11205                var item = $(this),
11206                selectedItem = $this.items.filter('.ui-state-highlight');
11207
11208                if(item.index() !== selectedItem.index()) {
11209                    if(selectedItem.length) {
11210                        $this.unselectItem(selectedItem);
11211                    }
11212
11213                    $this.selectItem(item);
11214                    $this.input.trigger('change');
11215                }
11216                
11217                /* For keyboard navigation */
11218                $this.removeOutline();
11219                $this.focusedItem = item;
11220                $this.input.trigger('focus');
11221
11222                $this.input.trigger('click');
11223
11224                PrimeFaces.clearSelection();
11225                e.preventDefault();
11226            });
11227        }
11228        
11229        this.bindKeyEvents();
11230    },
11231    
11232    bind
11232KeyEvents: function() {
11233        var $this = this;
11234
11235        this.input.off('focus.selectListbox blur.selectListbox keydown.selectListbox').on('focus.selectListbox', function(e) {
11236            $this.jq.addClass('ui-state-focus');
11237            
11238            var activeItem = $this.focusedItem||$this.items.filter('.ui-state-highlight:visible:first');
11239            if(activeItem.length) {
11240                $this.focusedItem = activeItem;
11241            }
11242            else {
11243                $this.focusedItem = $this.items.filter(':visible:first');
11244            }
11245            
11246            setTimeout(function() {
11247                if($this.focusedItem) {
11248                    PrimeFaces.scrollInView($this.listContainer, $this.focusedItem);
11249                    $this.focusedItem.addClass('ui-listbox-outline');
11250                }
11251            }, 100);
11252        })
11253        .on('blur.selectListbox', function() {
11254            $this.jq.removeClass('ui-state-focus');
11255            $this.removeOutline();
11256            $this.focusedItem = null;
11257        })
11258        .on('keydown.selectListbox', function(e) {
11259            if(!$this.focusedItem) {
11260                return;
11261            }
11262
11263            var keyCode = $.ui.keyCode,
11264                key = e.which;
11265
11266            switch(key) {
11267                case keyCode.UP:
11268                    if(!$this.focusedItem.hasClass('ui-state-highlight')) {
11269                        $this.focusedItem.trigger('click.selectListbox');
11270                    }
11271                    else {
11272                        var prevItem = $this.focusedItem.prevAll('.ui-selectlistbox-item:visible:first');
11273                        if(prevItem.length) {
11274                            prevItem.trigger('click.selectListbox');
11275
11276                            PrimeFaces.scrollInView($this.listContainer, $this.focusedItem);
11277                        }
11278                    }
11279                    e.preventDefault();
11280                break;
11281
11282                case keyCode.DOWN:
11283                    if(!$this.focusedItem.hasClass('ui-state-highlight')) {
11284                        $this.focusedItem.trigger('click.selectListbox');
11285                    }
11286                    else {
11287                        var nextItem = $this.focusedItem.nextAll('.ui-selectlistbox-item:visible:first');
11288                        if(nextItem.length) {
11289                            nextItem.trigger('click.selectListbox');
11290
11291                            PrimeFaces.scrollInView($this.listContainer, $this.focusedItem);
11292                        }
11293                    }
11294                    e.preventDefault();
11295                break;
11296            };
11297        });
11298
11299    },
11300    
11301    removeOutline: function() {
11302        if(this.focusedItem && this.focusedItem.hasClass('ui-listbox-outline')) {
11303            this.focusedItem.removeClass('ui-listbox-outline');
11304        }
11305    }
11306});
11307/**
11308 * PrimeFaces SelectManyMenu Widget
11309 */
11310PrimeFaces.widget.SelectManyMenu = PrimeFaces.widget.SelectListbox.extend({
11311
11312    init: function(cfg) {
11313        this._super(cfg);
11314
11315        this.allItems.filter('.ui-state-highlight').find('> .ui-chkbox > .ui-chkbox-box').addClass('ui-state-active');
11316    },
11317
11318    bindEvents: function() {
11319        this._super();
11320        var $this = this;
11321
11322        if(!this.cfg.disabled) {
11323            this.items.on('click.selectListbox', function(e) {
11324                //stop propagation
11325                if($this.checkboxClick) {
11326                    $this.checkboxClick = false;
11327                    return;
11328                }
11329
11330                var item = $(this),
11331                selectedItems = $this.items.filter('.ui-state-highlight'),
11332                metaKey = (e.metaKey||e.ctrlKey);
11333
11334                if(!e.shiftKey) {
11335                    if(!metaKey && !$this.cfg.showCheckbox) {
11336                        $this.unselectAll();
11337                    }
11338
11339                    if((metaKey || $this.cfg.showCheckbox) && item.hasClass('ui-state-highlight')) {
11340                        $this.unselectItem(item);
11341                    }
11342                    else {
11343                        $this.selectItem(item);
11344                        $this.cursorItem = item;
11345                    }
11346                }
11347                else {
11348                    //range selection
11349                    if($this.cursorItem) {
11350                        $this.unselectAll();
11351
11352                        var currentItemIndex = item.index(),
11353                        cursorItemIndex = $this.cursorItem.index(),
11354                        startIndex = (currentItemIndex > cursorItemIndex) ? cursorItemIndex : currentItemIndex,
11355                        endIndex = (currentItemIndex > cursorItemIndex) ? (currentItemIndex + 1) : (cursorItemIndex + 1);
11356
11357                        for(var i = startIndex ; i < endIndex; i++) {
11358                            var it = $this.allItems.eq(i);
11359
11360                            if(it.is(':visible') && !it.hasClass('ui-state-disabled')) {
11361                                $this.selectItem(it);
11362                            }
11363                        }
11364                    }
11365                    else {
11366                        $this.selectItem(item);
11367                        $this.cursorItem = item;
11368                    }
11369                }
11370
11371                $this.input.trigger('change');
11372                $this.input.trigger('click');
11373                PrimeFaces.clearSelection();
11374                e.preventDefault();
11375            });
11376
11377            if(this.cfg.showCheckbox) {
11378                this.checkboxes = this.jq.find('.ui-selectlistbox-item:not(.ui-state-disabled) div.ui-chkbox > div.ui-chkbox-box');
11379
11380                this.checkboxes.on('mouseover.selectManyMenu', function(e) {
11381                    var chkbox = $(this);
11382
11383                    if(!chkbox.hasClass('ui-state-active'))
11384                        chkbox.addClass('ui-state-hover');
11385                })
11386                .on('mouseout.selectManyMenu', function(e) {
11387                    $(this).removeClass('ui-state-hover');
11388                })
11389                .on('click.selectManyMenu', function(e) {
11390                    $this.checkboxClick = true;
11391
11392                    var item = $(this).closest('.ui-selectlistbox-item');
11393                    if(item.hasClass('ui-state-highlight'))
11394                        $this.unselectItem(item);
11395                    else
11396                        $this.selectItem(item);
11397
11398                    $this.input.trigger('change');
11399                });
11400            }
11401        }
11402    },
11403
11404    selectAll: function() {
11405        // ~~~ PERF ~~~
11406        // See https://github.com/primefaces/primefaces/issues/2089
11407        //
11408        // 1) don't use jquery wrappers
11409        // 2) don't use existing methods like selectItem
11410
11411        for(var i = 0; i < this.items.length; i++) {
11412            var item = this.items.eq(i);
11413            var itemNative = item[0];
11414
11415            itemNative.classList.add('ui-state-highlight');
11416            itemNative.classList.remove('ui-state-hover');
11417
11418            if(this.cfg.showCheckbox) {
11419                var checkbox = item.find('div.ui-chkbox').children('div.ui-chkbox-box');
11420
11421                var checkboxNative = checkbox[0];
11422                checkboxNative.classList.remove('ui-state-hover');
11423                checkboxNative.classList.add('ui-state-active');
11424
11425                var checkboxIconNative = checkbox.children('span.ui-chkbox-icon')[0];
11426                checkboxIconNative.classList.remove('ui-icon-blank');
11427                checkboxIconNative.classList.add('ui-icon-check');
11428            }
11429        }
11430
11431        for(var i = 0; i < this.options.length; i++) {
11432            this.options[i].selected = true;
11433        }
11434    },
11435
11436    unselectAll: function() {
11437        // ~~~ PERF ~~~
11438        // See https://github.com/primefaces/primefaces/issues/2089
11439        //
11440        // 1) don't use jquery wrappers
11441        // 2) don't use existing methods like unselectItem
11442
11443        for(var i = 0; i < this.items.length; i++) {
11444            var item = this.items.eq(i);
11445            var itemNative = item[0];
11446
11447            itemNative.classList.remove('ui-state-highlight');
11448
11449            if(this.cfg.showCheckbox) {
11450                var checkbox = item.find('div.ui-chkbox').children('div.ui-chkbox-box');
11451
11452                var checkboxNative = checkbox[0];
11453                checkboxNative.classList.remove('ui-state-active');
11454
11455                var checkboxIconNative = checkbox.children('span.ui-chkbox-icon')[0];
11456                checkboxIconNative.classList.add('ui-icon-blank');
11457                checkboxIconNative.classList.remove('ui-icon-check');
11458            }
11459        }
11460
11461        for(var i = 0; i < this.options.length; i++) {
11462            this.options[i].selected = false;
11463        }
11464    },
11465
11466    selectItem: function(item) {
11467        this._super(item);
11468
11469        if(this.cfg.showCheckbox) {
11470            this.selectCheckbox(item.find('div.ui-chkbox-box'));
11471        }
11472    },
11473
11474    unselectItem: function(item) {
11475        this._super(item);
11476
11477        if(this.cfg.showCheckbox) {
11478            this.unselectCheckbox(item.find('div.ui-chkbox-box'));
11479        }
11480    },
11481
11482    selectCheckbox: function(chkbox) {
11483        chkbox.removeClass('ui-state-hover').addClass('ui-state-active').children('span.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
11484    },
11485
11486    unselectCheckbox: function(chkbox) {
11487        chkbox.removeClass('ui-state-active').children('span.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
11488    }
11489});
11490
11491/**
11492 * PrimeFaces CommandButton Widget
11493 */
11494PrimeFaces.widget.CommandButton = PrimeFaces.widget.BaseWidget.extend({
11495
11496    init: function(cfg) {
11497        this._super(cfg);
11498
11499        PrimeFaces.skinButton(this.jq);
11500    },
11501
11502    disable: function() {
11503        this.jq.removeClass('ui-state-hover ui-state-focus ui-state-active')
11504                .addClass('ui-state-disabled').attr('disabled', 'disabled');
11505    },
11506
11507    enable: function() {
11508        this.jq.removeClass('ui-state-disabled').removeAttr('disabled');
11509    }
11510
11511});
11512/*
11513 * PrimeFaces Button Widget
11514 */
11515PrimeFaces.widget.Button = PrimeFaces.widget.BaseWidget.extend({
11516
11517    init: function(cfg) {
11518        this._super(cfg);
11519
11520        PrimeFaces.skinButton(this.jq);
11521    },
11522
11523    disable: function() {
11524        this.jq.removeClass('ui-state-hover ui-state-focus ui-state-active')
11525                .addClass('ui-state-disabled').attr('disabled', 'disabled');
11526    },
11527
11528    enable: function() {
11529        this.jq.removeClass('ui-state-disabled').removeAttr('disabled');
11530    }
11531
11532});
11533
11534/**
11535 * PrimeFaces LinkButton Widget
11536 */
11537PrimeFaces.widget.LinkButton = PrimeFaces.widget.BaseWidget.extend({
11538
11539    init: function(cfg) {
11540        this._super(cfg);
11541
11542        PrimeFaces.skinButton(this.jq);
11543    }
11544
11545});
11546/**
11547 * PrimeFaces SelecyManyButton Widget
11548 */
11549PrimeFaces.widget.SelectManyButton = PrimeFaces.widget.BaseWidget.extend({
11550
11551    init: function(cfg) {
11552        this._super(cfg);
11553
11554        this.buttons = this.jq.children('div:not(.ui-state-disabled)');
11555        this.inputs = this.jq.find(':checkbox:not(:disabled)');
11556        this.bindEvents();
11557
11558        //pfs metadata
11559        this.inputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
11560    },
11561
11562    bindEvents: function() {
11563        var $this = this;
11564
11565        this.buttons.on('mouseover', function() {
11566            var button = $(this);
11567            if(!button.hasClass('ui-state-active')) {
11568                button.addClass('ui-state-hover');
11569            }
11570        })
11571        .on('mouseout', function() {
11572            $(this).removeClass('ui-state-hover');
11573        })
11574        .on('click', function(e) {
11575            var button = $(this),
11576            input = button.children(':checkbox');
11577
11578            if(button.hasClass('ui-state-active'))
11579                button.addClass('ui-state-hover');
11580            else
11581                button.removeClass('ui-state-hover');
11582            
11583            input.trigger('focus').trigger('click');
11584        });
11585
11586        /* Keyboard support */
11587        this.inputs.on('focus', function() {
11588            var input = $(this),
11589            button = input.parent();
11590
11591            button.addClass('ui-state-focus');
11592        })
11593        .on('blur', function() {
11594            var input = $(this),
11595            button = input.parent();
11596
11597            button.removeClass('ui-state-focus');
11598        })
11599        .on('change', function() {
11600            var input = $(this),
11601            button = input.parent();
11602
11603            if(input.prop('checked'))
11604                button.addClass('ui-state-active');
11605            else
11606                button.removeClass('ui-state-active');
11607        })
11608        .on('click', function(e) {
11609            e.stopPropagation();
11610        });
11611    },
11612
11613    select: function(button) {
11614        button.children(':checkbox').prop('checked', true).change();
11615    },
11616
11617    unselect: function(button) {
11618        button.children(':checkbox').prop('checked', false).change();
11619    }
11620
11621});
11622
11623/**
11624 * PrimeFaces SelectOneButton Widget
11625 */
11626PrimeFaces.widget.SelectOneButton = PrimeFaces.widget.BaseWidget.extend({
11627
11628    init: function(cfg) {
11629        this._super(cfg);
11630
11631        this.buttons = this.jq.children('div:not(.ui-state-disabled)');
11632        this.inputs = this.jq.find(':radio:not(:disabled)');
11633        this.cfg.unselectable = this.cfg.unselectable === false ? false : true;
11634
11635        this.bindEvents();
11636
11637        //pfs metadata
11638        this.inputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
11639    },
11640
11641    bindEvents: function() {
11642        var $this = this;
11643
11644        this.buttons.on('mouseover', function() {
11645            var button = $(this);
11646            button.addClass('ui-state-hover');
11647        })
11648        .on('mouseout', function() {
11649            $(this).removeClass('ui-state-hover');
11650        })
11651        .on('click', function() {
11652            var button = $(this),
11653            radio = button.children(':radio');
11654
11655            if(button.hasClass('ui-state-active') || radio.prop('checked')) {
11656                $this.unselect(button);
11657            }
11658            else {
11659                $this.select(button);
11660            }
11661        });
11662
11663        /* For keyboard accessibility */
11664        this.buttons.on('focus.selectOneButton', function(){
11665            var button = $(this);
11666            button.addClass('ui-state-focus');
11667        })
11668        .on('blur.selectOneButton', function(){
11669            var button = $(this);
11670            button.removeClass('ui-state-focus');
11671        })
11672        .on('keydown.selectOneButton', function(e) {
11673            var keyCode = $.ui.keyCode,
11674            key = e.which;
11675
11676            if(key === keyCode.SPACE || key === keyCode.ENTER) {
11677                var button = $(this),
11678                radio = button.children(':radio');
11679
11680                if(radio.prop('checked')) {
11681                    $this.unselect(button);
11682                }
11683                else {
11684                    $this.select(button);
11685                }
11686                e.preventDefault();
11687            }
11688        });
11689    },
11690
11691    select: function(button) {
11692        this.buttons.filter('.ui-state-active').removeClass('ui-state-active ui-state-hover').children(':radio').prop('checked', false);
11693
11694        button.addClass('ui-state-active').children(':radio').prop('checked', true);
11695
11696        this.triggerChange();
11697    },
11698
11699    unselect: function(button) {
11700        if(this.cfg.unselectable) {
11701            button.removeClass('ui-state-active ui-state-hover').children(':radio').prop('checked', false).change();
11702
11703            this.triggerChange();
11704        }
11705    },
11706
11707    triggerChange: function() {
11708        if(this.cfg.change) {
11709            this.cfg.change.call(this);
11710        }
11711
11712        this.callBehavior('change');
11713    },
11714
11715    disable: function() {
11716        this.buttons.removeClass('ui-state-hover ui-state-focus ui-state-active')
11717                .addClass('ui-state-disabled').attr('disabled', 'disabled');
11718    },
11719
11720    enable: function() {
11721        this.buttons.removeClass('ui-state-disabled').removeAttr('disabled');
11722    }
11723
11724});
11725
11726/**
11727 * PrimeFaces SelectBooleanButton Widget
11728 */
11729PrimeFaces.widget.SelectBooleanButton = PrimeFaces.widget.BaseWidget.extend({
11730
11731    init: function(cfg) {
11732        this._super(cfg);
11733
11734        this.input = $(this.jqId + '_input');
11735        this.disabled = this.input.is(':disabled');
11736        this.icon = this.jq.children('.ui-button-icon-left');
11737        var $this = this;
11738
11739        //bind events if not disabled
11740        if(!this.disabled) {
11741            this.jq.on('mouseover', function() {
11742                if(!$this.jq.hasClass('ui-state-active')) {
11743                    $this.jq.addClass('ui-state-hover');
11744                }
11745            }).on('mouseout', function() {
11746                $this.jq.removeClass('ui-state-hover');
11747            })
11748            .on('click', function() {
11749                $this.toggle();
11750                $this.input.trigger('focus');
11751            });
11752        }
11753
11754        this.input.on('focus', function() {
11755            $this.jq.addClass('ui-state-focus');
11756        })
11757        .on('blur', function() {
11758            $this.jq.removeClass('ui-state-focus');
11759        })
11760        .on('keydown', function(e) {
11761            var keyCode = $.ui.keyCode;
11762            if(e.which === keyCode.SPACE) {
11763                e.preventDefault();
11764            }
11765        })
11766        .on('keyup', function(e) {
11767            var keyCode = $.ui.keyCode;
11768            if(e.which === keyCode.SPACE) {
11769                $this.toggle();
11770
11771                e.preventDefault();
11772            }
11773        });
11774
11775        //pfs metadata
11776        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
11777    },
11778
11779    toggle: function() {
11780        if(!this.disabled) {
11781            if(this.input.prop('checked'))
11782                this.uncheck();
11783            else
11784                this.check();
11785        }
11786    },
11787
11788    check: function() {
11789        if(!this.disabled) {
11790            this.input.prop('checked', true);
11791            this.jq.addClass('ui-state-active').children('.ui-button-text').text(this.cfg.onLabel);
11792
11793            if(this.icon.length > 0) {
11794                this.icon.removeClass(this.cfg.offIcon).addClass(this.cfg.onIcon);
11795            }
11796
11797            this.input.trigger('change');
11798        }
11799    },
11800
11801    uncheck: function() {
11802        if(!this.disabled) {
11803            this.input.prop('checked', false);
11804            this.jq.removeClass('ui-state-active').children('.ui-button-text').text(this.cfg.offLabel);
11805
11806            if(this.icon.length > 0) {
11807                this.icon.removeClass(this.cfg.onIcon).addClass(this.cfg.offIcon);
11808            }
11809
11810            this.input.trigger('change');
11811        }
11812    }
11813
11814});
11815
11816/**
11817 * PrimeFaces SelectCheckboxMenu Widget
11818 */
11819PrimeFaces.widget.SelectCheckboxMenu = PrimeFaces.widget.BaseWidget.extend({
11820
11821    init: function(cfg) {
11822        this._super(cfg);
11823
11824        this.labelContainer = this.jq.find('.ui-selectcheckboxmenu-label-container');
11825        this.label = this.jq.find('.ui-selectcheckboxmenu-label');
11826        this.menuIcon = this.jq.children('.ui-selectcheckboxmenu-trigger');
11827        this.triggers = this.jq.find('.ui-selectcheckboxmenu-trigger, .ui-selectcheckboxmenu-label');
11828        this.disabled = this.jq.hasClass('ui-state-disabled');
11829        this.inputs = this.jq.find(':checkbox');
11830        this.panelId = this.id + '_panel';
11831        this.labelId = this.id + '_label';
11832        this.keyboardTarget = $(this.jqId + '_focus');
11833        this.tabindex = this.keyboardTarget.attr('tabindex');
11834        this.cfg.showHeader = (this.cfg.showHeader === undefined) ? true : this.cfg.showHeader;
11835        this.cfg.dynamic = this.cfg.dynamic === true ? true : false;
11836        this.isDynamicLoaded = false;
11837        this.cfg.labelSeparator = (this.cfg.labelSeparator === undefined) ? ', ' : this.cfg.labelSeparator;
11838
11839        if(!this.disabled) {
11840            if(this.cfg.multiple) {
11841                this.triggers = this.jq.find('.ui-selectcheckboxmenu-trigger, .ui-selectcheckboxmenu-multiple-container');
11842            }
11843
11844            if(!this.cfg.dynamic) {
11845                this._renderPanel();
11846            }
11847
11848            this.bindEvents();
11849            this.bindKeyEvents();
11850
11851            //mark trigger and descandants of trigger as a trigger for a primefaces overlay
11852            this.triggers.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
11853
11854            if(!this.cfg.multiple) {
11855                if(this.cfg.updateLabel) {
11856                    this.defaultLabel = this.label.text();
11857                    this.label.css({
11858                        'text-overflow': 'ellipsis',
11859                        overflow: 'hidden'
11860                    });
11861
11862                    this.updateLabel();
11863                }
11864
11865                this.label.attr('id', this.labelId);
11866                this.keyboardTarget.attr('aria-expanded', false).attr('aria-labelledby', this.labelId);
11867            }
11868        } else {
11869            // disabled
11870            if(!this.cfg.multiple) {
11871                if (this.cfg.updateLabel) {
11872                    this.defaultLabel = this.label.text();
11873                    this.label.css({
11874                        'text-overflow': 'ellipsis',
11875                        overflow: 'hidden'
11876                    });
11877
11878                    this.updateLabel();
11879                }
11880            }
11881        }
11882
11883        //pfs metadata
11884        this.inputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
11885    },
11886
11887    //@override
11888    refresh: function(cfg) {
11889        this._super(cfg);
11890    },
11891
11892    _renderPanel: function() {
11893        this.renderPanel();
11894
11895        if(this.tabindex) {
11896            this.panel.find('a, input').attr('tabindex', this.tabindex);
11897        }
11898
11899        this.checkboxes = this.itemContainer.find('.ui-chkbox-box:not(.ui-state-disabled)');
11900        this.labels = this.itemContainer.find('label');
11901
11902        this.bindPanelEvents();
11903        this.bindPanelKeyEvents();
11904
11905        this.isDynamicLoaded = true;
11906    },
11907
11908    renderPanel: function() {
11909        this.panel = $('<div id="' + this.panelId + '" class="ui-selectcheckboxmenu-panel ui-widget ui-widget-content ui-corner-all ui-helper-hidden ui-input-overlay" role="dialog"></div>');
11910        this.cfg.appendTo = this.getAppendTo();
11911
11912        PrimeFaces.utils.registerDynamicOverlay(this, this.panel, this.id + '_panel');
11913
11914        if(this.cfg.panelStyle) {
11915            this.panel.attr('style', this.cfg.panelStyle);
11916        }
11917
11918        if(this.cfg.panelStyleClass) {
11919            this.panel.addClass(this.cfg.panelStyleClass);
11920        }
11921
11922        this.renderHeader();
11923
11924        this.renderItems();
11925
11926        if(this.cfg.scrollHeight) {
11927            this.itemContainerWrapper.height(this.cfg.scrollHeight);
11928        }
11929        else if(this.inputs.length > 10) {
11930            this.itemContainerWrapper.height(200);
11931        }
11932    },
11933
11934    renderHeader: function() {
11935        this.header = $('<div class="ui-widget-header ui-corner-all ui-selectcheckboxmenu-header ui-helper-clearfix"></div>')
11936                        .appendTo(this.panel);
11937
11938        if(!this.cfg.showHeader) {
11939            this.header.removeClass('ui-helper-clearfix').addClass('ui-helper-hidden');
11940        }
11941        //toggler
11942        this.toggler = $('<div class="ui-chkbox ui-widget"><div class="ui-helper-hidden-accessible"><input type="checkbox" role="checkbox" aria-label="Select All" readonly="readonly"></div><div class="ui-chkbox-box ui-widget ui-corner-all ui-state-default"><span class="ui-chkbox-icon ui-icon ui-icon-blank"></span></div></div>')
11943                            .appendTo(this.header);
11944        this.togglerBox = this.toggler.children('.ui-chkbox-box');
11945        if(this.inputs.filter(':not(:checked)').length === 0) {
11946            this.check(this.togglerBox);
11947        }
11948
11949        //filter
11950        if(this.cfg.filter) {
11951            this.filterInputWrapper = $('<div class="ui-selectcheckboxmenu-filter-container"></div>').appendTo(this.header);
11952            this.filterInput = $('<input type="text" aria-multiline="false" aria-readonly="false" aria-disabled="false" aria-label="Filter Input" role="textbox" class="ui-inputfield ui-inputtext ui-widget ui-state-default ui-corner-all">')
11953                                .appendTo(this.filterInputWrapper);
11954
11955            if(this.cfg.filterPlaceholder) {
11956                this.filterInput.attr('placeholder', this.cfg.filterPlaceholder);
11957            }
11958
11959            this.filterInputWrapper.append("<span class='ui-icon ui-icon-search'></span>");
11960        }
11961
11962        //closer
11963        this.closer = $('<a class="ui-selectcheckboxmenu-close ui-corner-all" href="#"><span class="ui-icon ui-icon-circle-close"></span></a>')
11964                    .attr('aria-label', 'Close').appendTo(this.header);
11965
11966    },
11967
11968    renderItems: function() {
11969        var $this = this;
11970
11971        this.itemContainerWrapper = $('<div class="ui-selectcheckboxmenu-items-wrapper"><ul class="ui-selectcheckboxmenu-items ui-selectcheckboxmenu-list ui-widget-content ui-widget ui-corner-all ui-helper-reset"></ul></div>')
11972                .appendTo(this.panel);
11973
11974        this.itemContainer = this.itemContainerWrapper.children('ul.ui-selectcheckboxmenu-items');
11975
11976        //check if inputs must be grouped
11977        var grouped = this.inputs.filter('[group-label]');
11978
11979        var currentGroupName = null;
11980        for(var i = 0; i < this.inputs.length; i++) {
11981            var input = this.inputs.eq(i),
11982            label = input.next(),
11983            disabled = input.is(':disabled'),
11984            checked = input.is(':checked'),
11985            title = input.attr('title'),
11986            boxClass = 'ui-chkbox-box ui-widget ui-corner-all ui-state-default',
11987            itemClass = 'ui-selectcheckboxmenu-item ui-selectcheckboxmenu-list-item ui-corner-all',
11988            escaped = input.data('escaped');
11989
11990            if(grouped.length && currentGroupName !== input.attr('group-label')) {
11991                currentGroupName = input.attr('group-label');
11992            	var itemGroup = $('<li class="ui-selectcheckboxmenu-item-group ui-selectcheckboxmenu-group-list-item ui-corner-all"></li>');
11993            	itemGroup.text(currentGroupName);
11994            	$this.itemContainer.append(itemGroup);
11995            }
11996
11997            if(disabled) {
11998                boxClass += " ui-state-disabled";
11999            }
12000
12001            if(checked) {
12002                boxClass += " ui-state-active";
12003            }
12004
12005            var iconClass = checked ? 'ui-chkbox-icon ui-icon ui-icon-check' : 'ui-chkbox-icon ui-icon ui-icon-blank',
12006            itemClass = checked ? itemClass + ' ui-selectcheckboxmenu-checked' : itemClass + ' ui-selectcheckboxmenu-unchecked';
12007
12008            var item = $('<li class="' + itemClass + '"></li>');
12009            item.append('<div class="ui-chkbox ui-widget"><div class="ui-helper-hidden-accessible"><input type="checkbox" role="checkbox" readonly="readonly"></input></div>' +
12010                    '<div class="' + boxClass + '"><span class="' + iconClass + '"></span></div></div>');
12011
12012            var uuid = PrimeFaces.uuid();
12013            var itemLabel = $('<label for='+uuid+'></label>'),
12014            labelHtml = label.html().trim(),
12015            labelLength = labelHtml.length;
12016            if (labelLength > 0 && labelHtml !== '&nbsp;')
12017                if(escaped)
12018                    itemLabel.text(label.text());
12019                else
12020                    itemLabel.html(label.html());
12021            else
12022                itemLabel.text(input.val());
12023
12024            itemLabel.appendTo(item);
12025
12026            if(title) {
12027                item.attr('title', title);
12028            }
12029
12030            if($this.cfg.multiple) {
12031                item.attr('data-item-value', input.val());
12032            }
12033
12034            item.find('> .ui-chkbox > .ui-helper-hidden-accessible > input').prop('checked', checked).attr('aria-checked', checked).attr('id', uuid);
12035            $this.itemContainer.attr('role', 'group');
12036
12037            $this.itemContainer.append(item);
12038        }
12039
12040        this.items = this.itemContainer.children('li.ui-selectcheckboxmenu-item');
12041        this.groupHeaders = this.itemContainer.children('li.ui-selectcheckboxmenu-item-group');
12042    },
12043
12044    bindEvents: function() {
12045        var $this = this;
12046
12047        //Events to show/hide the panel
12048        this.triggers.on('mouseover.selectCheckboxMenu', function() {
12049            if(!$this.disabled&&!$this.triggers.hasClass('ui-state-focus')) {
12050                $this.jq.addClass('ui-state-hover');
12051                $this.triggers.addClass('ui-state-hover');
12052            }
12053        }).on('mouseout.selectCheckboxMenu', function() {
12054            if(!$this.disabled) {
12055                $this.jq.removeClass('ui-state-hover');
12056                $this.triggers.removeClass('ui-state-hover');
12057            }
12058        }).on('mousedown.selectCheckboxMenu', function(e) {
12059            if(!$this.disabled) {
12060                if($this.cfg.multiple && $(e.target).is('.ui-selectcheckboxmenu-token-icon')) {
12061                    return;
12062                }
12063
12064                if($this.cfg.dynamic && !$this.isDynamicLoaded) {
12065                    $this._renderPanel();
12066                }
12067
12068                if($this.panel.is(":hidden")) {
12069                    $this.show();
12070                }
12071                else {
12072                    $this.hide(true);
12073                }
12074            }
12075        }).on('click.selectCheckboxMenu', function(e) {
12076            $this.jq.removeClass('ui-state-hover');
12077            $this.triggers.removeClass('ui-state-hover');
12078            $this.keyboardTarget.trigger('focus');
12079            e.preventDefault();
12080        });
12081
12082        if(this.cfg.multiple) {
12083            this.bindMultipleModeEvents();
12084        }
12085
12086        //Client Behaviors
12087        if(this.cfg.behaviors) {
12088            PrimeFaces.attachBehaviors(this.inputs, this.cfg.behaviors);
12089        }
12090    },
12091
12092    bindPanelEvents: function() {
12093        var $this = this;
12094
12095        //Events for checkboxes
12096        this.bindCheckboxHover(this.checkboxes);
12097        this.checkboxes.on('click.selectCheckboxMenu', function() {
12098            $this.toggleItem($(this));
12099        });
12100
12101        //Toggler
12102        this.bindCheckboxHover(this.togglerBox);
12103        this.togglerBox.on('click.selectCheckboxMenu', function() {
12104            var el = $(this);
12105            if(el.hasClass('ui-state-active')) {
12106                $this.uncheckAll();
12107                el.addClass('ui-state-hover');
12108            }
12109            else {
12110                $this.checkAll();
12111                el.removeClass('ui-state-hover');
12112            }
12113        });
12114
12115        //filter
12116        if(this.cfg.filter) {
12117            this.setupFilterMatcher();
12118
12119            PrimeFaces.skinInput(this.filterInput);
12120
12121            this.filterInput.on('keyup.selectCheckboxMenu', function() {
12122                $this.filter($(this).val());
12123            }).on('keydown.selectCheckboxMenu', function(e) {
12124                if(e.which === $.ui.keyCode.ESCAPE) {
12125                    $this.hide();
12126                }
12127            });
12128        }
12129
12130        //Closer
12131        this.closer.on('mouseenter.selectCheckboxMenu', function(){
12132            $(this).addClass('ui-state-hover');
12133        }
12133).on('mouseleave.selectCheckboxMenu', function() {
12134            $(this).removeClass('ui-state-hover');
12135        }).on('click.selectCheckboxMenu', function(e) {
12136            $this.hide(true);
12137
12138            e.preventDefault();
12139        });
12140
12141        //Labels
12142        this.labels.on('click.selectCheckboxMenu', function(e) {
12143            var checkbox = $(this).prev().children('.ui-chkbox-box');
12144            $this.toggleItem(checkbox);
12145            checkbox.removeClass('ui-state-hover');
12146            PrimeFaces.clearSelection();
12147            e.preventDefault();
12148        });
12149
12150        PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.panel,
12151            function() { return $this.triggers; },
12152            function(e, eventTarget) {
12153                if(!($this.panel.is(eventTarget) || $this.panel.has(eventTarget).length > 0)) {
12154                    $this.hide(true);
12155                }
12156            });
12157
12158        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.panel, function() {
12159            $this.alignPanel();
12160        });
12161    },
12162
12163    bindKeyEvents: function() {
12164        var $this = this;
12165
12166        this.keyboardTarget.on('focus.selectCheckboxMenu', function() {
12167            $this.jq.addClass('ui-state-focus');
12168            $this.menuIcon.addClass('ui-state-focus');
12169        }).on('blur.selectCheckboxMenu', function() {
12170            $this.jq.removeClass('ui-state-focus');
12171            $this.menuIcon.removeClass('ui-state-focus');
12172        }).on('keydown.selectCheckboxMenu', function(e) {
12173            var keyCode = $.ui.keyCode,
12174            key = e.which;
12175
12176            if($this.cfg.dynamic && !$this.isDynamicLoaded) {
12177                $this._renderPanel();
12178            }
12179
12180            switch(key) {
12181                case keyCode.ENTER:
12182                case keyCode.SPACE:
12183                    if ($this.panel.is(":hidden"))
12184                        $this.show();
12185                    else
12186                        $this.hide(true);
12187
12188                    e.preventDefault();
12189                break;
12190
12191                case keyCode.DOWN:
12192                    if (e.altKey) {
12193                        if ($this.panel.is(":hidden"))
12194                            $this.show();
12195                        else
12196                            $this.hide(true);
12197                    }
12198
12199                    e.preventDefault();
12200                break;
12201
12202                case keyCode.TAB:
12203                    if($this.panel.is(':visible')) {
12204                       if(!$this.cfg.showHeader) {
12205                            $this.itemContainer.children('li:not(.ui-state-disabled):first').find('div.ui-helper-hidden-accessible > input').trigger('focus');
12206                        }
12207                        else {
12208                            $this.toggler.find('> div.ui-helper-hidden-accessible > input').trigger('focus');
12209                        }
12210                        e.preventDefault();
12211                    }
12212
12213                break;
12214
12215                case keyCode.ESCAPE:
12216                    $this.hide();
12217                break;
12218            };
12219        });
12220    },
12221
12222    bindPanelKeyEvents: function() {
12223        var $this = this;
12224
12225        this.closer.on('focus.selectCheckboxMenu', function(e) {
12226            $this.closer.addClass('ui-state-focus');
12227        })
12228        .on('blur.selectCheckboxMenu', function(e) {
12229            $this.closer.removeClass('ui-state-focus');
12230        })
12231        .on('keydown.selectCheckboxMenu', function(e) {
12232            var keyCode = $.ui.keyCode,
12233            key = e.which;
12234
12235            switch(key) {
12236                case keyCode.ENTER:
12237                    $this.hide(true);
12238
12239                    e.preventDefault();
12240                break;
12241
12242                case keyCode.ESCAPE:
12243                    $this.hide();
12244                break;
12245            };
12246        });
12247
12248        var togglerCheckboxInput = this.toggler.find('> div.ui-helper-hidden-accessible > input');
12249        this.bindCheckboxKeyEvents(togglerCheckboxInput);
12250        togglerCheckboxInput.on('keyup.selectCheckboxMenu', function(e) {
12251                    if(e.which === $.ui.keyCode.SPACE) {
12252                        var input = $(this);
12253
12254                        if(input.prop('checked'))
12255                            $this.uncheckAll();
12256                        else
12257                            $this.checkAll();
12258
12259                        e.preventDefault();
12260                    }
12261                })
12262                .on('change.selectCheckboxMenu', function(e) {
12263                    var input = $(this);
12264
12265                    if(input.prop('checked')) {
12266                        $this.checkAll();
12267                    }
12268                    else {
12269                        $this.uncheckAll();
12270                    }
12271                });
12272
12273        var itemKeyInputs = this.itemContainer.find('> li > div.ui-chkbox > div.ui-helper-hidden-accessible > input');
12274        this.bindCheckboxKeyEvents(itemKeyInputs);
12275        itemKeyInputs.on('keyup.selectCheckboxMenu', function(e) {
12276                    if(e.which === $.ui.keyCode.SPACE) {
12277                        var input = $(this),
12278                        box = input.parent().next();
12279
12280                        if(input.prop('checked'))
12281                            $this.uncheck(box, true);
12282                        else
12283                            $this.check(box, true);
12284
12285                        e.preventDefault();
12286                    }
12287                })
12288                .on('change.selectCheckboxMenu', function(e) {
12289                    var input = $(this),
12290                    box = input.parent().next();
12291
12292                    if(input.prop('checked')) {
12293                        $this.check(box, true);
12294                    }
12295                    else {
12296                        $this.uncheck(box, true);
12297                    }
12298                });
12299    },
12300
12301    bindMultipleModeEvents: function() {
12302        var $this = this;
12303        this.multiItemContainer = this.jq.children('.ui-selectcheckboxmenu-multiple-container');
12304
12305        var closeSelector = '> li.ui-selectcheckboxmenu-token > .ui-selectcheckboxmenu-token-icon';
12306        this.multiItemContainer.off('click', closeSelector).on('click', closeSelector, null, function(e) {
12307            var itemValue = $(this).parent().data("item-value");
12308            var item = $this.items.filter('[data-item-value="' + $.escapeSelector(itemValue) +'"]');
12309            if(item && item.length) {
12310                if($this.cfg.dynamic && !$this.isDynamicLoaded) {
12311                    $this._renderPanel();
12312                }
12313
12314                $this.uncheck(item.children('.ui-chkbox').children('.ui-chkbox-box'), true);
12315
12316                if($this.hasBehavior('itemUnselect')) {
12317                    var ext = {
12318                        params : [
12319                            {name: $this.id + '_itemUnselect', value: itemValue}
12320                        ]
12321                    };
12322
12323                    $this.callBehavior('itemUnselect', ext);
12324                }
12325            }
12326
12327            e.stopPropagation();
12328        });
12329    },
12330
12331    bindCheckboxHover: function(item) {
12332        item.on('mouseenter.selectCheckboxMenu', function() {
12333            var item = $(this);
12334            if(!item.hasClass('ui-state-active')&&!item.hasClass('ui-state-disabled')) {
12335                item.addClass('ui-state-hover');
12336            }
12337        }
12337).on('mouseleave.selectCheckboxMenu', function() {
12338            $(this).removeClass('ui-state-hover');
12339        });
12340    },
12341
12342    filter: function(value) {
12343        var filterValue = this.cfg.caseSensitive ? $.trim(value) : $.trim(value).toLowerCase();
12344
12345        if(filterValue === '') {
12346            this.itemContainer.children('li.ui-selectcheckboxmenu-item').filter(':hidden').show();
12347        }
12348        else {
12349            for(var i = 0; i < this.labels.length; i++) {
12350                var labelElement = this.labels.eq(i),
12351                item = labelElement.parent(),
12352                itemLabel = this.cfg.caseSensitive ? labelElement.text() : labelElement.text().toLowerCase();
12353
12354                if(this.filterMatcher(itemLabel, filterValue)) {
12355                    item.show();
12356                }
12357                else {
12358                    item.hide();
12359                }
12360            }
12361        }
12362
12363        var groupHeaderLength = this.groupHeaders.length;
12364        for(var i = 0; i < groupHeaderLength; i++) {
12365            var header = $(this.groupHeaders[i]),
12366            groupedItems = header.nextUntil('li.ui-selectcheckboxmenu-item-group');
12367
12368            if(groupedItems.length === groupedItems.filter(':hidden').length) {
12369                header.hide();
12370            }
12371            else {
12372                header.show();
12373            }
12374        }
12375
12376        if(this.cfg.scrollHeight) {
12377            if(this.itemContainer.height() < this.cfg.initialHeight) {
12378                this.itemContainerWrapper.css('height', 'auto');
12379            }
12380            else {
12381                this.itemContainerWrapper.height(this.cfg.initialHeight);
12382            }
12383        }
12384
12385        this.updateToggler();
12386        this.alignPanel();
12387    },
12388
12389    setupFilterMatcher: function() {
12390        this.cfg.filterMatchMode = this.cfg.filterMatchMode||'startsWith';
12391        this.filterMatchers = {
12392            'startsWith': this.startsWithFilter
12393            ,'contains': this.containsFilter
12394            ,'endsWith': this.endsWithFilter
12395            ,'custom': this.cfg.filterFunction
12396        };
12397
12398        this.filterMatcher = this.filterMatchers[this.cfg.filterMatchMode];
12399    },
12400
12401    startsWithFilter: function(value, filter) {
12402        return value.indexOf(filter) === 0;
12403    },
12404
12405    containsFilter: function(value, filter) {
12406        return value.indexOf(filter) !== -1;
12407    },
12408
12409    endsWithFilter: function(value, filter) {
12410        return value.indexOf(filter, value.length - filter.length) !== -1;
12411    },
12412
12413    checkAll: function() {
12414        for(var i = 0; i < this.items.length; i++) {
12415            var el = this.items.eq(i);
12416
12417            if(el.is(':visible')) {
12418                var input = this.inputs.eq(i);
12419                var inputNative = input[0];
12420
12421                if(!inputNative.disabled) {
12422                    input.prop('checked', true).attr('aria-checked', true);
12423                    this.check(el.children('.ui-chkbox').children('.ui-chkbox-box'));
12424
12425                    if (this.cfg.multiple) {
12426                        this.createMultipleItem(el);
12427                    }
12428                }
12429            }
12430        }
12431
12432        this.check(this.togglerBox);
12433
12434        var togglerInput = this.togglerBox.prev().children('input');
12435        if(this.cfg.onChange) {
12436            this.cfg.onChange.call(this);
12437        }
12438
12439        if(!this.togglerBox.hasClass('ui-state-disabled')) {
12440            togglerInput.trigger('focus.selectCheckboxMenu');
12441            this.togglerBox.addClass('ui-state-active');
12442        }
12443
12444        if(this.cfg.multiple) {
12445            this.alignPanel();
12446        }
12447
12448        this.fireToggleSelectEvent(true);
12449    },
12450
12451    uncheckAll: function() {
12452        for(var i = 0; i < this.items.length; i++) {
12453            var el = this.items.eq(i);
12454
12455            if(el.is(':visible')) {
12456                var input = this.inputs.eq(i);
12457                var inputNative = input[0];
12458
12459                if(!inputNative.disabled) {
12460                    this.inputs.eq(i).prop('checked', false).attr('aria-checked', false);
12461                    this.uncheck(el.children('.ui-chkbox').children('.ui-chkbox-box'));
12462
12463                    if (this.cfg.multiple) {
12464                        this.multiItemContainer.children().remove();
12465                    }
12466                }
12467            }
12468        }
12469
12470        this.uncheck(this.togglerBox);
12471
12472        var togglerInput = this.togglerBox.prev().children('input');
12473        if(this.cfg.onChange) {
12474            this.cfg.onChange.call(this);
12475        }
12476
12477        if(!this.togglerBox.hasClass('ui-state-disabled')) {
12478            togglerInput.trigger('focus.selectCheckboxMenu');
12479        }
12480
12481        if(this.cfg.multiple) {
12482            this.alignPanel();
12483        }
12484
12485        this.fireToggleSelectEvent(false);
12486    },
12487
12488    fireToggleSelectEvent: function(checked) {
12489        if(this.hasBehavior('toggleSelect')) {
12490            var ext = {
12491                params: [{name: this.id + '_checked', value: checked}]
12492            };
12493
12494            this.callBehavior('toggleSelect', ext);
12495        }
12496    },
12497
12498    check: function(checkbox, updateInput) {
12499        if(!checkbox.hasClass('ui-state-disabled')) {
12500            var checkedInput = checkbox.prev().children('input'),
12501            item = checkbox.closest('li.ui-selectcheckboxmenu-item');
12502
12503            checkedInput.prop('checked', true).attr('aria-checked', true);
12504            if(updateInput) {
12505                checkedInput.trigger('focus.selectCheckboxMenu');
12506            }
12507
12508            checkbox.addClass('ui-state-active').children('.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
12509            item.removeClass('ui-selectcheckboxmenu-unchecked').addClass('ui-selectcheckboxmenu-checked');
12510
12511            if(updateInput) {
12512            	var itemGroups = item.prevAll('li.ui-selectcheckboxmenu-item-group'),
12513                input = this.inputs.eq(item.index() - itemGroups.length);
12514                input.prop('checked', true).attr('aria-checked', true).change();
12515
12516                this.updateToggler();
12517
12518                if(this.cfg.multiple) {
12519                    this.createMultipleItem(item);
12520                    this.alignPanel();
12521                }
12522            }
12523
12524            if(this.cfg.updateLabel) {
12525                this.updateLabel();
12526            }
12527        }
12528    },
12529
12530    uncheck: function(checkbox, updateInput) {
12531        if(!checkbox.hasClass('ui-state-disabled')) {
12532            var uncheckedInput = checkbox.prev().children('input'),
12533            item = checkbox.closest('li.ui-selectcheckboxmenu-item');
12534            checkbox.removeClass('ui-state-active').children('.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
12535            checkbox.closest('li.ui-selectcheckboxmenu-item').addClass('ui-selectcheckboxmenu-unchecked').removeClass('ui-selectcheckboxmenu-checked');
12536            uncheckedInput.prop('checked', false).attr('aria-checked', false);
12537
12538            if(updateInput) {
12539                var itemGroups = item.prevAll('li.ui-selectcheckboxmenu-item-group'),
12540                input = this.inputs.eq(item.index() - itemGroups.length);
12541                input.prop('checked', false).attr('aria-checked', false).change();
12542                uncheckedInput.trigger('focus.selectCheckboxMenu');
12543                this.updateToggler();
12544
12545                if(this.cfg.multiple) {
12546                    this.removeMultipleItem(item);
12547                    this.alignPanel();
12548                }
12549            }
12550
12551            if(this.cfg.updateLabel) {
12552                this.updateLabel();
12553            }
12554        }
12555    },
12556
12557    show: function() {
12558        this.panel.css({'display':'block', 'opacity':0, 'pointer-events': 'none'});
12559        
12560        this.alignPanel();
12561        
12562        this.panel.css({'display':'none', 'opacity':'', 'pointer-events': ''});
12563        
12564        this.keyboardTarget.attr('aria-expanded', true);
12565        this.panel.show();
12566
12567        this.postShow();
12568    },
12569
12570    hide: function(animate) {
12571        var $this = this;
12572
12573        this.keyboardTarget.attr('aria-expanded', false);
12574
12575        if(animate) {
12576            this.panel.fadeOut('fast', function() {
12577                $this.postHide();
12578            });
12579        }
12580
12581        else {
12582            this.panel.hide();
12583            this.postHide();
12584        }
12585    },
12586
12587    postShow: function() {
12588        if(this.cfg.onShow) {
12589            this.cfg.onShow.call(this);
12590        }
12591    },
12592
12593    postHide: function() {
12594        if(this.cfg.onHide) {
12595            this.cfg.onHide.call(this);
12596        }
12597    },
12598
12599    alignPanel: function() {
12600        var fixedPosition = this.panel.css('position') == 'fixed',
12601        win = $(window),
12602        positionOffset = fixedPosition ? '-' + win.scrollLeft() + ' -' + win.scrollTop() : null,
12603        panelStyle = this.panel.attr('style');
12604
12605        this.panel.css({
12606                'left':'',
12607                'top':'',
12608                'z-index': ++PrimeFaces.zindex
12609        });
12610
12611        if(this.panel.parent().attr('id') === this.id) {
12612            this.panel.css({
12613                left: 0,
12614                top: this.jq.innerHeight()
12615            });
12616        }
12617        else {
12618            this.panel.position({
12619                                my: 'left top'
12620                                ,at: 'left bottom'
12621                                ,of: this.jq
12622                                ,offset : positionOffset
12623                                ,collision: 'flipfit'
12624                            });
12625        }
12626
12627        if(!this.widthAligned && (this.panel.width() < this.jq.width()) && (!panelStyle||panelStyle.toLowerCase().indexOf('width') === -1)) {
12628            this.panel.width(this.jq.width());
12629            this.widthAligned = true;
12630        }
12631    },
12632
12633    toggleItem: function(checkbox) {
12634        if(!checkbox.hasClass('ui-state-disabled')) {
12635            if(checkbox.hasClass('ui-state-active')) {
12636                this.uncheck(checkbox, true);
12637                checkbox.addClass('ui-state-hover');
12638            }
12639            else {
12640                this.check(checkbox, true);
12641                checkbox.removeClass('ui-state-hover');
12642            }
12643        }
12644    },
12645
12646    updateToggler: function() {
12647        var visibleItems = this.itemContainer.children('li.ui-selectcheckboxmenu-item:visible');
12648
12649        if(visibleItems.length && visibleItems.filter('.ui-selectcheckboxmenu-unc
12649hecked').length === 0) {
12650            this.check(this.togglerBox);
12651        }
12652        else {
12653            this.uncheck(this.togglerBox);
12654        }
12655    },
12656
12657    bindCheckboxKeyEvents: function(items) {
12658        var $this = this;
12659
12660        items.on('focus.selectCheckboxMenu', function(e) {
12661            var input = $(this),
12662            box = input.parent().next();
12663
12664            box.addClass('ui-state-focus');
12665
12666            PrimeFaces.scrollInView($this.itemContainerWrapper, box);
12667        })
12668        .on('blur.selectCheckboxMenu', function(e) {
12669            var input = $(this),
12670            box = input.parent().next();
12671
12672            box.removeClass('ui-state-focus');
12673        })
12674        .on('keydown.selectCheckboxMenu', function(e) {
12675            var keyCode = $.ui.keyCode,
12676            key = e.which;
12677
12678            if(key === keyCode.SPACE) {
12679                e.preventDefault();
12680            }
12681            else if(key === keyCode.ESCAPE) {
12682                $this.hide();
12683            }
12684        });
12685    },
12686
12687    updateLabel: function() {
12688        var checkedItems = this.jq.find(':checked'),
12689            labelText = '';
12690
12691        if(checkedItems && checkedItems.length) {
12692            for(var i = 0; i < checkedItems.length; i++) {
12693                if(i != 0) {
12694                    labelText = labelText + this.cfg.labelSeparator;
12695                }
12696                labelText = labelText + $(checkedItems[i]).next().text();
12697            }
12698        }
12699        else {
12700            if (this.cfg.emptyLabel) {
12701                labelText = this.cfg.emptyLabel;
12702            } else {
12703                labelText = this.defaultLabel;
12704            }
12705        }
12706
12707        this.label.text(labelText);
12708        this.labelContainer.attr('title', labelText);
12709    },
12710
12711    createMultipleItem: function(item) {
12712        var items = this.multiItemContainer.children();
12713        if(items.length && items.filter('[data-item-value="' + $.escapeSelector(item.data('item-value')) + '"]').length > 0) {
12714            return;
12715        }
12716
12717        var itemGroups = item.prevAll('li.ui-selectcheckboxmenu-item-group'),
12718        input = this.inputs.eq(item.index() - itemGroups.length),
12719        escaped = input.data('escaped'),
12720        labelHtml = input.next().html().trim(),
12721        labelLength = labelHtml.length,
12722        label = labelLength > 0 && labelHtml !== '&nbsp;' ? (escaped ? PrimeFaces.escapeHTML(input.next().text()) : input.next().html()) : PrimeFaces.escapeHTML(input.val()),
12723        itemDisplayMarkup = '<li class="ui-selectcheckboxmenu-token ui-state-active ui-corner-all" data-item-value="' + PrimeFaces.escapeHTML(input.val()) +'">';
12724        itemDisplayMarkup += '<span class="ui-selectcheckboxmenu-token-icon ui-icon ui-icon-close"></span>';
12725        itemDisplayMarkup += '<span class="ui-selectcheckboxmenu-token-label">' + label + '</span></li>';
12726
12727        this.multiItemContainer.append(itemDisplayMarkup);
12728    },
12729
12730    removeMultipleItem: function(item) {
12731        var items = this.multiItemContainer.children();
12732        if(items.length) {
12733            items.filter('[data-item-value="' + $.escapeSelector(item.data('item-value')) + '"]').remove();
12734        }
12735    },
12736
12737    selectValue: function(value) {                                                                     // Patch
12738        var idx = -1;
12739        // find input-index
12740        for(var i = 0; i < this.inputs.length; i++) {
12741            if (this.inputs.eq(i).val() === value) {
12742                idx = i;
12743                break;
12744            }
12745        }
12746        if (idx === -1) {
12747            return;
12748        }
12749        var input = this.inputs.eq(idx);   // the hidden input
12750        var item  = this.items.eq(idx);    // the Overlay-Panel-Item (li)
12751
12752        // check (see this.checkAll())
12753        input.prop('checked', true).attr('aria-checked', true);
12754        this.check(item.children('.ui-chkbox').children('.ui-chkbox-box'));
12755
12756        if(this.cfg.multiple) {
12757            this.createMultipleItem(item);
12758        }
12759    },
12760    
12761    getAppendTo: function() {
12762        var dialog = this.jq.closest('.ui-dialog');
12763
12764        if(dialog.length == 1) {
12765            //set position as fixed to scroll with dialog
12766            if(dialog.css('position') === 'fixed') {
12767                this.panel.css('position', 'fixed');
12768            }
12769
12770            //append to body if not already appended by user choice
12771            if(!this.panel.parent().is(document.body)) {
12772                return "@(body)";
12773            }
12774        }
12775
12776        return this.cfg.appendTo;
12777    }
12778
12779});
12780
12781/**
12782 * PrimeFaces InputMask Widget
12783 */
12784PrimeFaces.widget.InputMask = PrimeFaces.widget.BaseWidget.extend({
12785
12786    init: function(cfg) {
12787        this._super(cfg);
12788
12789        if(this.cfg.mask) {
12790            var originalOnchange = this.jq.prop('onchange');
12791            if(originalOnchange) {
12792                this.cfg.onChange = originalOnchange;
12793                this.jq.prop('onchange', null);
12794            }
12795            
12796            this.jq.mask(this.cfg.mask, this.cfg);
12797        }
12798
12799        //Visuals
12800        PrimeFaces.skinInput(this.jq);
12801    },
12802
12803    setValue: function(value) {
12804        this.jq.val(value);
12805        this.jq.unmask().mask(this.cfg.mask, this.cfg);
12806    },
12807
12808    getValue: function() {
12809        return this.jq.val();
12810    }
12811
12812});
12813
12814/**
12815 * PrimeFaces Password
12816 */
12817PrimeFaces.widget.Password = PrimeFaces.widget.BaseWidget.extend({
12818
12819    init: function(cfg) {
12820        this._super(cfg);
12821
12822        if(!this.jq.is(':disabled')) {
12823            if(this.cfg.feedback) {
12824                this.setupFeedback();
12825            }
12826
12827            PrimeFaces.skinInput(this.jq);
12828        }
12829    },
12830
12831    setupFeedback: function() {
12832        var _self = this;
12833
12834        //remove previous panel if any
12835        var oldPanel = $(this.jqId + '_panel');
12836        if(oldPanel.length == 1) {
12837            oldPanel.remove();
12838        }
12839
12840        //config
12841        this.cfg.promptLabel = this.cfg.promptLabel||'Please enter a password';
12842        this.cfg.weakLabel = this.cfg.weakLabel||'Weak';
12843        this.cfg.goodLabel = this.cfg.goodLabel||'Medium';
12844        this.cfg.strongLabel = this.cfg.strongLabel||'Strong';
12845
12846        var panelStyle = this.cfg.inline ? 'ui-password-panel-inline' : 'ui-password-panel-overlay';
12847
12848        //create panel element
12849        var panelMarkup = '<div id="' + this.id + '_panel" class="ui-password-panel ui-widget ui-state-highlight ui-corner-all ui-helper-hidden ' + panelStyle + '">';
12850        panelMarkup += '<div class="ui-password-meter" style="background-position:0pt 0pt">&nbsp;</div>';
12851        panelMarkup += '<div class="ui-password-info">' + PrimeFaces.escapeHTML(this.cfg.promptLabel) + '</div>';
12852        panelMarkup += '</div>';
12853
12854        this.panel = $(panelMarkup).insertAfter(this.jq);
12855        this.meter = this.panel.children('div.ui-password-meter');
12856        this.infoText = this.panel.children('div.ui-password-info');
12857
12858        if(!this.cfg.inline) {
12859            this.panel.addClass('ui-shadow');
12860        }
12861
12862        //events
12863        this.jq.focus(function() {
12864            _self.show();
12865        })
12866        .blur(function() {
12867            _self.hide();
12868        })
12869        .keyup(function() {
12870            var value = _self.jq.val(),
12871            label = null,
12872            meterPos = null;
12873
12874            if(value.length == 0) {
12875                label = _self.cfg.promptLabel;
12876                meterPos = '0px 0px';
12877            }
12878            else {
12879                var score = _self.testStrength(_self.jq.val());
12880
12881                if(score < 30) {
12882                    label = _self.cfg.weakLabel;
12883                    meterPos = '0px -10px';
12884                }
12885                else if(score >= 30 && score < 80) {
12886                    label = _self.cfg.goodLabel;
12887                    meterPos = '0px -20px';
12888                }
12889                else if(score >= 80) {
12890                    label = _self.cfg.strongLabel;
12891                    meterPos = '0px -30px';
12892                }
12893            }
12894
12895            //update meter and info text
12896            _self.meter.css('background-position', meterPos);
12897            _self.infoText.text(label);
12898        });
12899
12900        //overlay setting
12901        if(!this.cfg.inline) {
12902            this.panel.appendTo('body');
12903
12904            //Hide overlay on resize
12905            PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', _self.panel, function() {
12906                _self.align();
12907            });
12908        }
12909    },
12910
12911    testStrength: function(password) {
12912        // return a number between 0 and 100.
12913        var score = 0;
12914
12915        // must be at least 8 characters
12916        if (!password || password.length < 8)
12917            return score;
12918
12919
12920        // require 3 of the following 4 categories
12921        var variations = {
12922            digits : /\d/.test(password),
12923            lower : /[a-z]/.test(password),
12924            upper : /[A-Z]/.test(password),
12925            nonWords : /\W/.test(password)
12926        }
12927
12928        variationCount = 0;
12929        for ( var check in variations) {
12930            variationCount += (variations[check] == true) ? 1 : 0;
12931        }
12932        score += variationCount * 28;
12933
12934        return parseInt(score);
12935    },
12936
12937    normalize: function(x, y) {
12938        var diff = x - y;
12939
12940        if(diff <= 0) {
12941            return x / y;
12942        }
12943        else {
12944            return 1 + 0.5 * (x / (x + y/4));
12945        }
12946    },
12947
12948    align: function() {
12949        this.panel.css({
12950            left:'',
12951            top:'',
12952            'z-index': ++PrimeFaces.zindex
12953        })
12954        .position({
12955            my: 'left top',
12956            at: 'right top',
12957            of: this.jq
12958        });
12959    },
12960
12961    show: function() {
12962        if(!this.cfg.inline) {
12963            this.align();
12964
12965            this.panel.fadeIn();
12966        }
12967        else {
12968            this.panel.slideDown();
12969        }
12970    },
12971
12972    hide: function() {
12973        if(this.cfg.inline)
12974            this.panel.slideUp();
12975        else
12976            this.panel.fadeOut();
12977    }
12978
12979});
12980
12981/**
12982 * PrimeFaces DefaultCommand Widget
12983 */
12984PrimeFaces.widget.DefaultCommand = PrimeFaces.widget.BaseWidget.extend({
12985
12986    init: function(cfg) {
12987        this.cfg = cfg;
12988        this.id = this.cfg.id;
12989        this.jqId = PrimeFaces.escapeClientId(this.id);
12990        this.jqTarget = $(PrimeFaces.escapeClientId(this.cfg.target));
12991        this.scope = this.cfg.scope ? $(PrimeFaces.escapeClientId(this.cfg.scope)) : null;
12992        var $this = this;
12993
12994        // container support - e.g. splitButton
12995        if (this.jqTarget.is(':not(:button):not(:input):not(a)')) {
12996            this.jqTarget = this.jqTarget.find('button,a').filter(':visible').first();
12997        }
12998
12999        //attach keypress listener to parent form
13000        var closestForm = this.jqTarget.closest('form');
13001        closestForm.off('keydown.' + this.id).on('keydown.' + this.id, {scopeEnter: false}, function (e, data) {
13002            var keyCode = $.ui.keyCode;
13003
13004            data = data || e.data;
13005            if (($this.scope && data.scopeEnter && data.scopeDefaultCommandId === $this.id)
13006                    || (!$this.scope && !data.scopeEnter && (e.which == keyCode.ENTER))) {
13007                //do not proceed if target is a textarea,button or link
13008                if ($(e.target).is('textarea,button,input[type="submit"],a')) {
13009                    return true;
13010                }
13011
13012                if (!$this.jqTarget.is(':disabled, .ui-state-disabled')) {
13013                    $this.jqTarget.trigger(PrimeFaces.csp.clickEvent());
13014                }
13015                e.preventDefault();
13016                e.stopImmediatePropagation();
13017            }
13018        });
13019
13020        if (this.scope) {
13021            this.scope.off('keydown.' + this.id).on('keydown.' + this.id, function (e) {
13022                var keyCode = $.ui.keyCode;
13023                if (e.which == keyCode.ENTER) {
13024                    closestForm.trigger(e, {scopeEnter: true, scopeDefaultCommandId: $this.id});
13025                    //e.preventDefault();
13026                    e.stopPropagation();
13027                }
13028            });
13029        }
13030
13031        this.removeScriptElement(this.id);
13032    }
13033});
13034
13035/*
13036 * PrimeFaces SplitButton Widget
13037 */
13038PrimeFaces.widget.SplitButton = PrimeFaces.widget.BaseWidget.extend({
13039
13040    init: function(cfg) {
13041        this._super(cfg);
13042        this.button = $(this.jqId + '_button');
13043        this.menuButton = $(this.jqId + '_menuButton');
13044        this.menuId = this.jqId + "_menu";
13045        this.menu = $(this.menuId);
13046        this.menuitemContainer = this.menu.find('.ui-menu-list');
13047        this.menuitems = this.menuitemContainer.children('.ui-menuitem:not(.ui-state-disabled)');
13048        this.cfg.disabled = this.button.is(':disabled');
13049
13050        if(!this.cfg.disabled) {
13051            this.bindEvents();
13052
13053            PrimeFaces.utils.registerDynamicOverlay(this, this.menu, this.id + '_menu');
13054        }
13055
13056        //pfs metadata
13057        this.button.data(PrimeFaces.CLIENT_ID_DATA, this.id);
13058        this.menuButton.data(PrimeFaces.CLIENT_ID_DATA, this.id);
13059    },
13060
13061    //@override
13062    refresh: function(cfg) {
13063        this._super(cfg);
13064    },
13065
13066    bindEvents: function() {
13067        var $this = this;
13068
13069        PrimeFaces.skinButton(this.button).skinButton(this.menuButton);
13070
13071        //mark button and descandants of button as a trigger for a primefaces overlay
13072        this.button.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
13073
13074        //toggle menu
13075        this.menuButton.click(function() {
13076            if($this.menu.is(':hidden'))
13077                $this.show();
13078            else
13079                $this.hide();
13080        });
13081
13082        //menuitem visuals
13083        this.menuitems.mouseover(function(e) {
13084            var menuitem = $(this),
13085            menuitemLink = menuitem.children('.ui-menuitem-link');
13086
13087            if(!menuitemLink.hasClass('ui-state-disabled')) {
13088                menuitem.addClass('ui-state-hover');
13089            }
13090        }).mouseout(function(e) {
13091            $(this).removeClass('ui-state-hover');
13092        }).click(function() {
13093            $this.hide();
13094        });
13095
13096        //keyboard support
13097        this.menuButton.keydown(function(e) {
13098            var keyCode = $.ui.keyCode;
13099
13100            switch(e.which) {
13101                case keyCode.UP:
13102                    $this.highlightPrev(e);
13103                break;
13104
13105                case keyCode.DOWN:
13106                    $this.highlightNext(e);
13107                break;
13108
13109                case keyCode.ENTER:
13110                case keyCode.SPACE:
13111                    $this.handleEnterKey(e);
13112                break;
13113
13114
13115                case keyCode.ESCAPE:
13116                case keyCode.TAB:
13117                    $this.handleEscapeKey();
13118                break;
13119            }
13120        });
13121
13122        PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.menu, null,
13123            function(e, eventTarget) {
13124                if(!($this.menu.is(eventTarget) || $this.menu.has(eventTarget).length > 0)) {
13125                    $this.button.removeClass('ui-state-focus ui-state-hover');
13126                    $this.hide();
13127                }
13128            });
13129
13130        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.menu, function() {
13131            $this.alignPanel();
13132        });
13133        
13134        if(this.cfg.filter) {
13135            this.setupFilterMatcher();
13136            this.filterInput = this.menu.find('> div.ui-splitbuttonmenu-filter-container > input.ui-splitbuttonmenu-filter');
13137            PrimeFaces.skinInput(this.filterInput);
13138
13139            this.bindFilterEvents();
13140        }
13141    },
13142    
13143    bindFilterEvents: function() {
13144        var $this = this;
13145
13146        this.filterInput.on('keyup.ui-splitbutton', function(e) {
13147            var keyCode = $.ui.keyCode,
13148            key = e.which;
13149
13150            switch(key) {
13151                case keyCode.UP:
13152                case keyCode.LEFT:
13153                case keyCode.DOWN:
13154                case keyCode.RIGHT:
13155                case keyCode.ENTER:
13156                case keyCode.TAB:
13157                case keyCode.ESCAPE:
13158                case keyCode.SPACE:
13159                case keyCode.HOME:
13160                case keyCode.PAGE_DOWN:
13161                case keyCode.PAGE_UP:
13162                case keyCode.END:
13163                case 16: //shift
13164                case 17: //keyCode.CONTROL:
13165                case 18: //keyCode.ALT:
13166                case 91: //left window or cmd:
13167                case 92: //right window:
13168                case 93: //right cmd:
13169                case 20: //capslock:
13170                break;
13171
13172                default:
13173                    //function keys (F1,F2 etc.)
13174                    if(key >= 112 && key <= 123) {
13175                        break;
13176                    }
13177
13178                    var metaKey = e.metaKey||e.ctrlKey;
13179
13180                    if(!metaKey) {
13181                        $this.filter($(this).val());
13182                    }
13183                break;
13184            }
13185        })
13186        .on('keydown.ui-splitbutton',function(e) {
13187            var keyCode = $.ui.keyCode,
13188            key = e.which;
13189
13190            switch(key) {
13191                case keyCode.UP:
13192                    $this.highlightPrev(e);
13193                break;
13194
13195                case keyCode.DOWN:
13196                    $this.highlightNext(e);
13197                break;
13198                
13199                case keyCode.ENTER:
13200                    $this.handleEnterKey(e);
13201                break;
13202                
13203                case keyCode.SPACE:
13204                    var target = $(e.target);
13205
13206                    if(target.is('input') && target.hasClass('ui-splitbuttonmenu-filter')) {
13207                        return;
13208                    }
13209                    
13210                    $this.handleEnterKey(e);
13211                break;
13212                
13213                case keyCode.ESCAPE:
13214                case keyCode.TAB:
13215                    $this.handleEscapeKey();
13216                break;
13217
13218                default:
13219                break;
13220            }
13221        }).on('paste.ui-splitbutton', function() {
13222            setTimeout(function(){
13223                $this.filter($this.filterInput.val());
13224            },2);
13225	});
13226    },
13227    
13228    highlightNext: function(event) {
13229        var highlightedItem = this.menuitems.filter('.ui-state-hover'),
13230        nextItems = highlightedItem.length ? highlightedItem.nextAll(':not(.ui-separator, .ui-widget-header):visible') : this.menuitems.filter(':visible').eq(0);
13231        
13232        if(nextItems.length) {
13233            highlightedItem.removeClass('ui-state-hover');
13234            nextItems.eq(0).addClass('ui-state-hover');
13235        }
13236
13237        event.preventDefault();
13238    },
13239    
13240    highlightPrev: function(event) {
13241        var highlightedItem = this.menuitems.filter('.ui-state-hover'),
13242        prevItems = highlightedItem.length ? highlightedItem.prevAll(':not(.ui-separator, .ui-widget-header):visible') : null;
13243
13244        if(prevItems && prevItems.length) {
13245            highlightedItem.removeClass('ui-state-hover');
13246            prevItems.eq(0).addClass('ui-state-hover');
13247        }
13248
13249        event.preventDefault();
13250    },
13251    
13252    handleEnterKey: function(event) {
13253        if(this.menu.is(':visible')) {
13254            var link = this.menuitems.filter('.ui-state-hover').children('a');
13255            link.trigger('click');
13256            
13257            var href = link.attr('href');
13258            if(href && href !== '#') {
13259                window.location.href = href;
13260            } 
13261        }
13262        else {
13263            this.show();
13264        }
13265
13266        event.preventDefault();
13267    },
13268    
13269    handleEscapeKey: function() {
13270        this.hide();
13271    },
13272    
13273    setupFilterMatcher: function() {
13274        this.cfg.filterMatchMode = this.cfg.filterMatchMode||'startsWith';
13275        this.filterMatchers = {
13276            'startsWith': this.startsWithFilter
13277            ,'contains': this.containsFilter
13278            ,'endsWith': this.endsWithFilter
13279            ,'custom': this.cfg.filterFunction
13280        };
13281
13282        this.filterMatcher = this.filterMatchers[this.cfg.filterMatchMode];
13283    },
13284
13285    startsWithFilter: function(value, filter) {
13286        return value.indexOf(filter) === 0;
13287    },
13288
13289    containsFilter: function(value, filter) {
13290        return value.indexOf(filter) !== -1;
13291    },
13292
13293    endsWithFilter: function(value, filter) {
13294        return value.indexOf(filter, value.length - filter.length) !== -1;
13295    },
13296
13297    filter: function(value) {
13298        var filterValue = $.trim(value).toLowerCase();
13299
13300        if(filterValue === '') {
13301            this.menuitems.filter(':hidden').show();
13302            this.menuitemContainer.children('.ui-widget-header').show();
13303            this.menuitemContainer.children('.ui-separator').show();
13304        }
13305        else {
13306            for(var i = 0; i < this.menuitems.length; i++) {
13307                var menuitem = this.menuitems.eq(i),
13308                itemLabel = menuitem.find('.ui-menuitem-text').text().toLowerCase();
13309
13310                /* for keyboard support */
13311                menuitem.removeClass('ui-state-hover');
13312                
13313                if(this.filterMatcher(itemLabel, filterValue))
13314                    menuitem.show();
13315                else
13316                    menuitem.hide();
13317
13318            }
13319
13320            //groups
13321            var groupHeaders = this.menuitemContainer.children('.ui-widget-header');
13322            for(var g = 0; g < groupHeaders.length; g++) {
13323                var group = groupHeaders.eq(g);
13324
13325                if(g === (groupHeaders.length - 1)) {
13326                    if(group.nextAll('.ui-submenu-child').filter(':visible').length === 0)
13327                        group.hide();
13328                    else
13329                        group.show();
13330                }
13331                else {
13332                    if(group.nextUntil('.ui-widget-header').filter(':visible').length === 0)
13333                        group.hide();
13334                    else
13335                        group.show();
13336                }
13337            }
13338            
13339            var separators = this.menuitemContainer.children('.ui-separator');
13340            for(var s = 0; s < separators.length; s++) {
13341                var separator = separators.eq(s);
13342                if(separator.nextAll().filter(':visible').length === 0 || separator.prevAll().filter(':visible').length === 0) {
13343                    separator.hide();
13344                }
13345                else {
13346                    separator.show();
13347                }
13348            }
13349        }
13350        
13351        this.alignPanel();
13352    },
13353
13354    show: function() {
13355        this.jq.attr('aria-expanded', true);
13356        this.alignPanel();
13357
13358        this.menu.show();
13359        
13360        if(this.cfg.filter) {
13361            this.filterInput.focus();
13362        }
13363        else {
13364            this.menuButton.focus();
13365        }
13366    },
13367
13368    hide: function() {
13369        this.jq.attr('aria-expanded', false);
13370        this.menuitems.filter('.ui-state-hover').removeClass('ui-state-hover');
13371        this.menuButton.removeClass('ui-state-focus');
13372
13373        this.menu.fadeOut('fast');
13374    },
13375
13376    alignPanel: function() {
13377        this.menu.css({left:'', top:'','z-index': ++PrimeFaces.zindex});
13378
13379        if(this.menu.parent().is(this.jq)) {
13380            this.menu.css({
13381                left: 0,
13382                top: this.jq.innerHeight()
13383            });
13384        }
13385        else {
13386            this.menu.position({
13387                my: 'left top'
13388                ,at: 'left bottom'
13389                ,of: this.button
13390            });
13391        }
13392    }
13393
13394});
13395
13396/*
13397 * PrimeFaces ThemeSwitcher Widget
13398 */
13399PrimeFaces.widget.ThemeSwitcher = PrimeFaces.widget.SelectOneMenu.extend({
13400
13401    init: function(cfg) {
13402        this._super(cfg);
13403
13404        var $this = this;
13405        this.input.on('change', function() {
13406            PrimeFaces.changeTheme($this.getSelectedValue());
13407        });
13408    }
13409});
13410
13411/*
13412 * PrimeFaces MultiSelectListbox Widget
13413 */
13414PrimeFaces.widget.MultiSelectListbox = PrimeFaces.widget.BaseWidget.extend({
13415
13416    init: function(cfg) {
13417       this._super(cfg);
13418
13419       this.root = this.jq.children('div.ui-multiselectlistbox-listcontainer');
13420       this.items = this.jq.find('li.ui-multiselectlistbox-item');
13421       this.input = $(this.jqId + '_input');
13422       this.cfg.disabled = this.jq.hasClass('ui-state-disabled');
13423
13424       if(!this.cfg.disabled) {
13425           this.bindEvents();
13426       }
13427
13428       var value = this.input.val();
13429       if(value !== '') {
13430           this.preselect(value);
13431       }
13432    },
13433
13434    bindEvents: function() {
13435       var $this = this;
13436
13437       this.items.on('mouseover.multiSelectListbox', function() {
13438           var item = $(this);
13439
13440           if(!item.hasClass('ui-state-highlight'))
13441               $(this).addClass('ui-state-hover');
13442       })
13443       .on('mouseout.multiSelectListbox', function() {
13444           var item = $(this);
13445
13446           if(!item.hasClass('ui-state-highlight'))
13447               $(this).removeClass('ui-state-hover');
13448       })
13449       .on('click.multiSelectListbox', function() {
13450           var item = $(this);
13451
13452           if(!item.hasClass('ui-state-highlight')){
13453               $this.showOptionGroup(item);
13454           }
13455       });
13456    },
13457
13458    unbindEvents: function() {
13459       this.items.off('mouseover.multiSelectListbox mouseout.multiSelectListbox click.multiSelectListbox');
13460    },
13461
13462    showOptionGroup: function(item) {
13463       item.addClass('ui-state-highlight').removeClass('ui-state-hover').siblings().filter('.ui-state-highlight').removeClass('ui-state-highlight');
13464       item.closest('.ui-multiselectlistbox-listcontainer').nextAll().remove();
13465       this.input.val(item.attr('data-value'));
13466
13467       var childItemsContainer = item.children('ul');
13468
13469       if(childItemsContainer.length) {
13470           var groupContainer = $('<div class="ui-multiselectlistbox-listcontainer" style="display:none"></div>');
13471           childItemsContainer.clone(true).appendTo(groupContainer).addClass('ui-multiselectlistbox-list ui-inputfield ui-widget-content').removeClass('ui-helper-hidden');
13472
13473           if(this.cfg.showHeaders) {
13474               groupContainer.prepend('<div class="ui-multiselectlistbox-header ui-widget-header ui-corner-top">' + PrimeFaces.escapeHTML(item.children('span').text()) + '</div>')
13475                       .children('.ui-multiselectlistbox-list').addClass('ui-corner-bottom');
13476           } else {
13477               groupContainer.children().addClass('ui-corner-all');
13478           }
13479
13480           this.jq.append(groupContainer);
13481
13482           if(this.cfg.effect)
13483               groupContainer.show(this.cfg.effect);
13484           else
13485               groupContainer.show();
13486       }
13487       else {
13488           this.triggerChange();
13489       }
13490    },
13491
13492    enable: function() {
13493       if(this.cfg.disabled) {
13494           this.cfg.disabled = false;
13495           this.jq.removeClass('ui-state-disabled');
13496           this.bindEvents();
13497       }
13498
13499    },
13500
13501    disable: function() {
13502       if(!this.cfg.disabled) {
13503           this.cfg.disabled = true;
13504           this.jq.addClass('ui-state-disabled');
13505           this.unbindEvents();
13506           this.root.nextAll().remove();
13507       }
13508    },
13509
13510    preselect: function(value) {
13511        var $this = this,
13512        item = this.items.filter('[data-value="' + $.escapeSelector(value)+'"]');
13513
13514        if(item.length === 0) {
13515            return;
13516        }
13517
13518        var ancestors = item.parentsUntil('.ui-multiselectlistbox-list'),
13519        selectedIndexMap = [];
13520
13521        for(var i = (ancestors.length - 1); i >= 0; i--) {
13522            var ancestor = ancestors.eq(i);
13523
13524            if(ancestor.is('li')) {
13525                selectedIndexMap.push(ancestor.index());
13526            }
13527            else if(ancestor.is('ul')) {
13528                var groupContainer = $('<div class="ui-multiselectlistbox-listcontainer" style="display:none"></div>');
13529                ancestor.clone(true).appendTo(groupContainer).addClass('ui-multiselectlistbox-list ui-inputfield ui-widget-content ui-corner-all').removeClass('ui-helper-hidden');
13530
13531                if(this.cfg.showHeaders) {
13532                   groupContainer.prepend('<div class="ui-multiselectlistbox-header ui-widget-header ui-corner-top">' + PrimeFaces.escapeHTML(ancestor.prev('span').text()) + '</div>')
13533                           .children('.ui-multiselectlistbox-list').addClass('ui-corner-bottom').removeClass('ui-corner-all');
13534                }
13535
13536                $this.jq.append(groupContainer);
13537            }
13538        }
13539
13540        //highlight item
13541        var lists = this.jq.children('div.ui-multiselectlistbox-listcontainer'),
13542        clonedItem = lists.find(' > ul.ui-multiselectlistbox-list > li.ui-multiselectlistbox-item').filter('[data-value="' + $.escapeSelector(value) + '"]');
13543        clonedItem.addClass('ui-state-highlight');
13544
13545        //highlight ancestors
13546        for(var i = 0; i < selectedIndexMap.length; i++) {
13547            lists.eq(i).find('> .ui-multiselectlistbox-list > li.ui-multiselectlistbox-item').eq(selectedIndexMap[i]).addClass('ui-state-highlight');
13548        }
13549
13550        $this.jq.children('div.ui-multiselectlistbox-listcontainer:hidden').show();
13551    },
13552
13553    triggerChange: function () {
13554        this.callBehavior('change');
13555    }
13556});
13557
13558/**
13559 * PrimeFaces Growl Widget
13560 */
13561PrimeFaces.widget.Growl = PrimeFaces.widget.BaseWidget.extend({
13562
13563    init: function(cfg) {
13564        this.cfg = cfg;
13565        this.id = this.cfg.id;
13566        this.jqId = PrimeFaces.escapeClientId(this.id);
13567
13568        this.render();
13569
13570        this.removeScriptElement(this.id);
13571    },
13572
13573    //Override
13574    refresh: function(cfg) {
13575    	this.cfg = cfg;
13576        this.show(cfg.msgs);
13577
13578        this.removeScriptElement(this.id);
13579    },
13580
13581    show: function(msgs) {
13582        var $this = this;
13583
13584        this.jq.css('z-index', ++PrimeFaces.zindex);
13585
13586        if(!this.cfg.keepAlive) {
13587            //clear previous messages
13588            this.removeAll();
13589        }
13590
13591        $.each(msgs, function(index, msg) {
13592            $this.renderMessage(msg);
13593        });
13594    },
13595
13596    removeAll: function() {
13597        this.jq.children('div.ui-growl-item-container').remove();
13598    },
13599
13600    render: function() {
13601        //create container
13602        this.jq = $('<div id="' + this.id + '_container" class="ui-growl ui-widget" aria-live="polite"></div>');
13603        this.jq.appendTo($(document.body));
13604
13605        //render messages
13606        this.show(this.cfg.msgs);
13607    },
13608
13609    renderMessage: function(msg) {
13610        var markup = '<div class="ui-growl-item-container ui-state-highlight ui-corner-all ui-helper-hidden ui-shadow ui-growl-' + msg.severity + '">';
13611        markup += '<div role="alert" class="ui-growl-item">';
13612        markup += '<div class="ui-growl-icon-close ui-icon ui-icon-closethick" style="display:none"></div>';
13613        markup += '<span class="ui-growl-image ui-growl-image-' + msg.severity + '" ></span>';
13614        if (msg.severityText) {
13615            // GitHub #5153 for screen readers
13616            markup += '<span class="ui-growl-severity ui-helper-hidden-accessible">' + msg.severityText + '</span>';
13617        }
13618        markup += '<div class="ui-growl-message">';
13619        markup += '<span class="ui-growl-title"></span>';
13620        markup += '<p></p>';
13621        markup += '</div><div style="clear: both;"></div></div></div>';
13622
13623        var message = $(markup),
13624        summaryEL = message.find('span.ui-growl-title'),
13625        detailEL = summaryEL.next();
13626
13627        if(this.cfg.escape) {
13628            summaryEL.text(msg.summary);
13629            detailEL.text(msg.detail);
13630        }
13631        else {
13632            summaryEL.html(msg.summary);
13633            detailEL.html(msg.detail);
13634        }
13635
13636        this.bindEvents(message);
13637
13638        message.appendTo(this.jq).fadeIn();
13639    },
13640
13641    bindEvents: function(message) {
13642        var $this = this,
13643        sticky = this.cfg.sticky;
13644
13645        message.mouseover(function() {
13646            var msg = $(this);
13647
13648            //visuals
13649            if(!msg.is(':animated')) {
13650                msg.find('div.ui-growl-icon-close:first').show();
13651            }
13652
13653            // clear hide timeout on mouseover
13654            if(!sticky) {
13655                clearTimeout(msg.data('timeout'));
13656            }
13657        })
13658        .mouseout(function() {
13659            //visuals
13660            $(this).find('div.ui-growl-icon-close:first').hide();
13661
13662            // setup hide timeout again after mouseout
13663            if(!sticky) {
13664                $this.setRemovalTimeout(message);
13665            }
13666        });
13667
13668        //remove message on click of close icon
13669        message.find('div.ui-growl-icon-close').click(function() {
13670            $this.removeMessage(message);
13671
13672            //clear timeout if removed manually
13673            if(!sticky) {
13674                clearTimeout(message.data('timeout'));
13675            }
13676        });
13677
13678        //hide the message after given time if not sticky
13679        if(!sticky) {
13680            this.setRemovalTimeout(message);
13681        }
13682    },
13683
13684    removeMessage: function(message) {
13685        message.fadeTo('normal', 0, function() {
13686            message.slideUp('normal', 'easeInOutCirc', function() {
13687                message.remove();
13688            });
13689        });
13690    },
13691
13692    setRemovalTimeout: function(message) {
13693        var $this = this;
13694
13695        var timeout = setTimeout(function() {
13696            $this.removeMessage(message);
13697        }, this.cfg.life);
13698
13699        message.data('timeout', timeout);
13700    }
13701});
13702/**
13703 * PrimeFaces Inplace Widget
13704 */
13705PrimeFaces.widget.Inplace = PrimeFaces.widget.BaseWidget.extend({
13706
13707    init: function(cfg) {
13708        this._super(cfg);
13709
13710        this.display = $(this.jqId + '_display');
13711        this.content = $(this.jqId + '_content');
13712        this.cfg.formId = this.jq.parents('form:first').attr('id');
13713
13714        var $this = this;
13715
13716        if(!this.cfg.disabled) {
13717
13718            if(this.cfg.toggleable) {
13719                this.display.bind(this.cfg.event, function(){
13720                    $this.show();
13721                });
13722
13723                this.display.mouseover(function(){
13724                    $(this).toggleClass("ui-state-highlight");
13725                }).mouseout(function(){
13726                    $(this).toggleClass("ui-state-highlight");
13727                });
13728            }
13729            else {
13730                this.display.css('cursor', 'default');
13731            }
13732
13733            if(this.cfg.editor) {
13734                this.cfg.formId = $(this.jqId).parents('form:first').attr('id');
13735
13736                this.editor = $(this.jqId + '_editor');
13737
13738                var saveButton = this.editor.children('.ui-inplace-save'),
13739                cancelButton = this.editor.children('.ui-inplace-cancel');
13740
13741                PrimeFaces.skinButton(saveButton).skinButton(cancelButton);
13742
13743                saveButton.click(function(e) {$this.save(e)});
13744                cancelButton.click(function(e) {$this.cancel(e)});
13745            }
13746
13747            /* to enter space in inplace input within multi-selection dataTable */
13748            this.content.find('input:text,textarea').on('keydown.inplace-text', function(e) {
13749                var keyCode = $.ui.keyCode;
13750
13751                if(e.which === keyCode.SPACE) {
13752                    e.stopPropagation();
13753                }
13754            });
13755        }
13756    },
13757
13758    show: function() {
13759        this.toggle(this.content, this.display);
13760    },
13761
13762    hide: function() {
13763        this.toggle(this.display, this.content);
13764    },
13765
13766    toggle: function(elToShow, elToHide) {
13767        var $this = this;
13768
13769        if(this.cfg.effect === 'fade') {
13770            elToHide.fadeOut(this.cfg.effectSpeed,
13771                function() {
13772                    elToShow.fadeIn($this.cfg.effectSpeed);
13773                    $this.postShow();
13774                });
13775        }
13776        else if(this.cfg.effect === 'slide') {
13777            elToHide.slideUp(this.cfg.effectSpeed,
13778                function() {
13779                    elToShow.slideDown($this.cfg.effectSpeed);
13780                    $this.postShow();
13781            });
13782        }
13783        else if(this.cfg.effect === 'none') {
13784            elToHide.hide();
13785            elToShow.show();
13786            $this.postShow();
13787        }
13788    },
13789
13790    postShow: function() {
13791        this.content.find('input:text,textarea').filter(':visible:enabled:first').focus().select();
13792
13793        PrimeFaces.invokeDeferredRenders(this.id);
13794    },
13795
13796    getDisplay: function() {
13797        return this.display;
13798    },
13799
13800    getContent: function() {
13801        return this.content;
13802    },
13803
13804    save: function(e) {
13805        var options = {
13806            source: this.id,
13807            update: this.id,
13808            process: this.id,
13809            formId: this.cfg.formId
13810        };
13811
13812        if(this.hasBehavior('save')) {
13813            this.callBehavior('save', options);
13814        }
13815        else {
13816            PrimeFaces.ajax.Request.handle(options);
13817        }
13818    },
13819
13820    cancel: function(e) {
13821        var options = {
13822            source: this.id,
13823            update: this.id,
13824            process: this.id,
13825            formId: this.cfg.formId
13826        };
13827
13828        options.params = [
13829            {name: this.id + '_cancel', value: true}
13830        ];
13831
13832        if(this.hasBehavior('cancel')) {
13833            this.callBehavior('cancel', options);
13834        }
13835        else {
13836            PrimeFaces.ajax.Request.handle(options);
13837        }
13838    }
13839
13840});
13841
13842/**
13843 * PrimeFaces LightBox Widget
13844 */
13845PrimeFaces.widget.LightBox = PrimeFaces.widget.BaseWidget.extend({
13846
13847    init: function(cfg) {
13848        // the dynamic overlay must be appended to the body
13849        cfg.appendTo = '@(body)';
13850
13851        this._super(cfg);
13852
13853        this.links = this.jq.children(':not(.ui-lightbox-inline)');
13854
13855        this.createPanel();
13856
13857        if(this.cfg.mode === 'image') {
13858            this.setupImaging();
13859        } else if(this.cfg.mode === 'inline') {
13860            this.setupInline();
13861        } else if(this.cfg.mode === 'iframe') {
13862            this.setupIframe();
13863        }
13864
13865        this.bindCommonEvents();
13866
13867        if(this.cfg.visible) {
13868            this.links.eq(0).click();
13869        }
13870    },
13871
13872    //@override
13873    refresh: function(cfg) {
13874        PrimeFaces.utils.removeDynamicOverlay(this, this.panel, this.id + '_panel', $(document.body));
13875
13876        this._super(cfg);
13877    },
13878
13879    createPanel: function() {
13880        this.panel = $('<div id="' + this.id + '_panel" class="ui-lightbox ui-widget ui-helper-hidden ui-corner-all ui-sha
13880dow">'
13881            + '<div class="ui-lightbox-content-wrapper">'
13882            + '<a class="ui-state-default ui-lightbox-nav-left ui-corner-right ui-helper-hidden"><span class="ui-icon ui-icon-carat-1-w">go</span></a>'
13883            + '<div class="ui-lightbox-content ui-corner-all"></div>'
13884            + '<a class="ui-state-default ui-lightbox-nav-right ui-corner-left ui-helper-hidden"><span class="ui-icon ui-icon-carat-1-e">go</span></a>'
13885            + '</div>'
13886            + '<div class="ui-lightbox-caption ui-widget-header"><span class="ui-lightbox-caption-text"></span>'
13887            + '<a class="ui-lightbox-close ui-corner-all" href="#"><span class="ui-icon ui-icon-closethick"></span></a><div style="clear:both"></div></div>'
13888            + '</div>');
13889
13890        PrimeFaces.utils.registerDynamicOverlay(this, this.panel, this.id + '_panel');
13891
13892        this.contentWrapper = this.panel.children('.ui-lightbox-content-wrapper');
13893        this.content = this.contentWrapper.children('.ui-lightbox-content');
13894        this.caption = this.panel.children('.ui-lightbox-caption');
13895        this.captionText = this.caption.children('.ui-lightbox-caption-text');
13896        this.closeIcon = this.caption.children('.ui-lightbox-close');
13897    },
13898
13899    setupImaging: function() {
13900        var $this = this;
13901
13902        this.content.append('<img class="ui-helper-hidden"></img>');
13903        this.imageDisplay = this.content.children('img');
13904        this.navigators = this.contentWrapper.children('a');
13905
13906        this.imageDisplay.on('load', function() {
13907            var image = $(this);
13908
13909            $this.scaleImage(image);
13910
13911            //coordinates to center overlay
13912            var leftOffset = ($this.panel.width() - image.width()) / 2,
13913            topOffset = ($this.panel.height() - image.height()) / 2;
13914
13915            //resize content for new image
13916            $this.content.removeClass('ui-lightbox-loading');
13917            $this.content.stop().animate({ width: image.width(), height: image.height() },
13918                500,
13919                function() {
13920                    //show image
13921                    image.fadeIn();
13922                    $this.showNavigators();
13923                    $this.caption.slideDown();
13924                });
13925
13926            $this.panel.stop().animate({ left: '+=' + leftOffset, top: '+=' + topOffset}, 500);
13927        });
13928
13929        this.navigators.mouseover(function() {
13930            $(this).addClass('ui-state-hover');
13931        })
13932        .mouseout(function() {
13933            $(this).removeClass('ui-state-hover');
13934        })
13935        .click(function(e) {
13936            var nav = $(this);
13937
13938            $this.hideNavigators();
13939
13940            if(nav.hasClass('ui-lightbox-nav-left')) {
13941                var index = $this.current == 0 ? $this.links.length - 1 : $this.current - 1;
13942
13943                $this.links.eq(index).trigger('click');
13944            }
13945            else {
13946                var index = $this.current == $this.links.length - 1 ? 0 : $this.current + 1;
13947
13948                $this.links.eq(index).trigger('click');
13949            }
13950
13951            e.preventDefault();
13952        });
13953
13954        this.links.click(function(e) {
13955            var link = $(this);
13956
13957            if($this.isHidden()) {
13958                $this.content.addClass('ui-lightbox-loading').width(32).height(32);
13959                $this.show();
13960            }
13961            else {
13962                $this.imageDisplay.stop().fadeOut(function() {
13963                    //clear for onload scaling
13964                    $(this).css({
13965                        'width': 'auto'
13966                        ,'height': 'auto'
13967                    });
13968
13969                    $this.content.addClass('ui-lightbox-loading');
13970                });
13971
13972                $this.caption.stop().slideUp();
13973            }
13974
13975            clearTimeout(this.timeout);
13976            this.timeout = setTimeout(function() {
13977                $this.imageDisplay.attr('src', link.attr('href'));
13978                $this.current = link.index();
13979
13980                var title = link.attr('title');
13981                if(title) {
13982                    $this.captionText.text(title);
13983                }
13984            }, 1000);
13985
13986            e.preventDefault();
13987        });
13988    },
13989
13990    scaleImage: function(image) {
13991        var win = $(window),
13992        winWidth = win.width(),
13993        winHeight = win.height(),
13994        imageWidth = image.width(),
13995        imageHeight = image.height(),
13996        ratio = imageHeight / imageWidth;
13997
13998        if(imageWidth >= winWidth && ratio <= 1){
13999            imageWidth = winWidth * 0.75;
14000            imageHeight = imageWidth * ratio;
14001        }
14002        else if(imageHeight >= winHeight){
14003            imageHeight = winHeight * 0.75;
14004            imageWidth = imageHeight / ratio;
14005        }
14006
14007        image.css({
14008            'width':imageWidth + 'px'
14009            ,'height':imageHeight + 'px'
14010        });
14011    },
14012
14013    setupInline: function() {
14014        this.inline = this.jq.children('.ui-lightbox-inline');
14015        this.inline.appendTo(this.content).show();
14016        var $this = this;
14017
14018        this.links.click(function(e) {
14019            $this.show();
14020
14021            var title = $(this).attr('title');
14022            if(title) {
14023                $this.captionText.text(title);
14024                $this.caption.stop().slideDown();
14025            }
14026
14027            e.preventDefault();
14028        });
14029    },
14030
14031    setupIframe: function() {
14032        var $this = this;
14033        this.iframeLoaded = false;
14034        this.cfg.width = this.cfg.width||'640px';
14035        this.cfg.height = this.cfg.height||'480px';
14036
14037        this.iframe = $('<iframe frameborder="0" style="width:' + this.cfg.width + ';height:'
14038                        + this.cfg.height + ';border:0 none; display: block;"></iframe>').appendTo(this.content);
14039
14040        if(this.cfg.iframeTitle) {
14041            this.iframe.attr('title', this.cfg.iframeTitle);
14042        }
14043
14044        this.links.click(function(e) {
14045            if(!$this.iframeLoaded) {
14046                $this.content.addClass('ui-lightbox-loading').css({
14047                    width: $this.cfg.width
14048                    ,height: $this.cfg.height
14049                });
14050                $this.show();
14051
14052                $this.iframe.on('load', function() {
14053                                $this.iframeLoaded = true;
14054                                $this.content.removeClass('ui-lightbox-loading');
14055                            })
14056                            .attr('src', $this.links.eq(0).attr('href'));
14057            }
14058            else {
14059                $this.show();
14060            }
14061
14062            var title = $this.links.eq(0).attr('title');
14063            if(title) {
14064                $this.captionText.text(title);
14065                $this.caption.slideDown();
14066            }
14067
14068            e.preventDefault();
14069        });
14070    },
14071
14072    bindCommonEvents: function() {
14073        var $this = this;
14074
14075        this.closeIcon.mouseover(function() {
14076            $(this).addClass('ui-state-hover');
14077        })
14078        .mouseout(function() {
14079            $(this).removeClass('ui-state-hover');
14080        });
14081
14082        this.closeIcon.click(function(e) {
14083            $this.hide();
14084            e.preventDefault();
14085        });
14086
14087        var hideEvent = PrimeFaces.env.ios ? 'touchstart' : 'click';
14088        PrimeFaces.utils.registerHideOverlayHandler(this, hideEvent + '.' + this.id + '_hide', $this.panel,
14089            function() { return $this.links.add($this.closeIcon); },
14090            function(e, eventTarget) {
14091                if(!($this.panel.is(eventTarget) || $this.panel.has(eventTarget).length > 0)) {
14092                    e.preventDefault();
14093                    $this.hide();
14094                }
14095            });
14096
14097        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.panel, function() {
14098            $(document.body).children('.ui-widget-overlay').css({
14099                'width': $(document).width()
14100                ,'height': $(document).height()
14101            });
14102        });
14103    },
14104
14105    show: function() {
14106        this.center();
14107
14108        this.panel.css('z-index', ++PrimeFaces.zindex).show();
14109
14110        if(!PrimeFaces.utils.isModalActive(this.id)) {
14111            this.enableModality();
14112        }
14113
14114        if(this.cfg.onShow) {
14115            this.cfg.onShow.call(this);
14116        }
14117    },
14118
14119    hide: function() {
14120        this.panel.fadeOut();
14121        this.disableModality();
14122        this.caption.hide();
14123
14124        if(this.cfg.mode == 'image') {
14125            this.imageDisplay.hide().attr('src', '').removeAttr('style');
14126            this.hideNavigators();
14127        }
14128
14129        if(this.cfg.onHide) {
14130            this.cfg.onHide.call(this);
14131        }
14132    },
14133
14134    center: function() {
14135        var win = $(window),
14136        left = (win.width() / 2 ) - (this.panel.width() / 2),
14137        top = (win.height() / 2 ) - (this.panel.height() / 2);
14138
14139        this.panel.css({
14140            'left': left,
14141            'top': top
14142        });
14143    },
14144
14145    enableModality: function() {
14146        PrimeFaces.utils.addModal(this, this.panel.css('z-index') - 1);
14147    },
14148
14149    disableModality: function() {
14150        PrimeFaces.utils.removeModal(this);
14151    },
14152
14153    showNavigators: function() {
14154        this.navigators.zIndex(this.imageDisplay.zIndex() + 1).show();
14155    },
14156
14157    hideNavigators: function() {
14158        this.navigators.hide();
14159    },
14160
14161    addOnshowHandler: function(fn) {
14162        this.onshowHandlers.push(fn);
14163    },
14164
14165    isHidden: function() {
14166        return this.panel.is(':hidden');
14167    },
14168
14169    showURL: function(opt) {
14170        if(opt.width)
14171            this.iframe.attr('width', opt.width);
14172        if(opt.height)
14173            this.iframe.attr('height', opt.height);
14174
14175        this.iframe.attr('src', opt.src);
14176
14177        this.captionText.text(opt.title||'');
14178        this.caption.slideDown();
14179
14180        this.show();
14181    }
14182
14183});
14184
14185PrimeFaces.widget.Menu = PrimeFaces.widget.BaseWidget.extend({
14186
14187    init: function(cfg) {
14188        this._super(cfg);
14189
14190        if(this.cfg.overlay) {
14191            this.initOverlay();
14192        }
14193
14194        this.keyboardTarget = this.jq.children('.ui-helper-hidden-accessible');
14195    },
14196
14197    initOverlay: function() {
14198        var $this = this;
14199
14200        this.jq.addClass('ui-menu-overlay');
14201
14202        this.cfg.trigger = this.cfg.trigger.replace(/\\\\:/g,"\\:");
14203
14204        this.trigger = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.cfg.trigger);
14205
14206        //mark trigger and descandants of trigger as a trigger for a primefaces overlay
14207        this.trigger.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
14208
14209        this.cfg.appendTo = '@(body)';
14210        PrimeFaces.utils.registerDynamicOverlay(this, this.jq, this.id);
14211
14212        this.cfg.pos = {
14213            my: this.cfg.my,
14214            at: this.cfg.at,
14215            of: this.trigger,
14216            collision: this.cfg.collision || "flip"
14217        };
14218
14219        this.trigger.off(this.cfg.triggerEvent + '.ui-menu').on(this.cfg.triggerEvent + '.ui-menu', function(e) {
14220            var trigger = $(this);
14221
14222            if($this.jq.is(':visible')) {
14223                $this.hide();
14224            }
14225            else {
14226                $this.show();
14227
14228                if(trigger.is(':button')) {
14229                    trigger.addClass('ui-state-focus');
14230                }
14231
14232                e.preventDefault();
14233            }
14234        });
14235
14236        //hide overlay on document click
14237        this.itemMouseDown = false;
14238
14239        PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.jq,
14240            function() { return $this.trigger; },
14241            function(e, eventTarget) {
14242                var menuItemLink = '.ui-menuitem-link:not(.ui-submenu-link, .ui-state-disabled)';
14243
14244                if (eventTarget.is(menuItemLink) || eventTarget.closest(menuItemLink).length) {
14245                    $this.itemMouseDown = true;
14246                }
14247                else if(!($this.jq.is(eventTarget) || $this.jq.has(eventTarget).length > 0)) {
14248                    $this.hide(e);
14249                }
14250            });
14251
14252        var hideUpNS = 'mouseup.' + this.id;
14253        $(document.body).off(hideUpNS).on(hideUpNS, function (e) {
14254            if($this.itemMouseDown) {
14255                $this.hide(e);
14256                $this.itemMouseDown = false;
14257            }
14258        });
14259
14260        //Hide overlay on resize
14261        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.jq, function() {
14262            $this.align();
14263        });
14264
14265        //dialog support
14266        this.setupDialogSupport();
14267    },
14268
14269    setupDialogSupport: function() {
14270        var dialog = this.trigger.parents('.ui-dialog:first');
14271
14272        if(dialog.length == 1 && dialog.css('position') === 'fixed') {
14273            this.jq.css('position', 'fixed');
14274        }
14275    },
14276
14277    show: function() {
14278        this.jq.css({
14279            'z-index': ++PrimeFaces.zindex,
14280            'visibility': 'hidden'
14281        });
14282        this.align();
14283        this.jq.show();
14284        this.jq.css('visibility', '');
14285    },
14286
14287    hide: function() {
14288        this.jq.fadeOut('fast');
14289
14290        if(this.trigger && this.trigger.is(':button')) {
14291            this.trigger.removeClass('ui-state-focus');
14292        }
14293    },
14294
14295    align: function() {
14296        this.jq.css({left:'', top:''}).position(this.cfg.pos);
14297    }
14298});
14299
14300
14301
14302
14303
14304
14305PrimeFaces.widget.TieredMenu = PrimeFaces.widget.Menu.extend({
14306
14307    init: function(cfg) {
14308        this._super(cfg);
14309
14310        this.cfg.toggleEvent = this.cfg.toggleEvent||'hover';
14311        this.links = this.jq.find('a.ui-menuitem-link:not(.ui-state-disabled)');
14312        this.rootLinks = this.jq.find('> ul.ui-menu-list > .ui-menuitem > .ui-menuitem-link');
14313
14314        this.bindEvents();
14315    },
14316
14317    bindEvents: function() {
14318        this.bindItemEvents();
14319        this.bindKeyEvents();
14320        this.bindDocumentHandler();
14321    },
14322
14323    bindItemEvents: function() {
14324        if(this.cfg.toggleEvent === 'hover')
14325            this.bindHoverModeEvents();
14326        else if(this.cfg.toggleEvent === 'click')
14327            this.bindClickModeEvents();
14328    },
14329
14330    bindHoverModeEvents: function() {
14331        var $this = this;
14332
14333        this.links.mouseenter(function() {
14334            var link = $(this),
14335            menuitem = link.parent();
14336
14337            var activeSibling = menuitem.siblings('.ui-menuitem-active');
14338            if(activeSibling.length === 1) {
14339                activeSibling.find('li.ui-menuitem-active').each(function() {
14340                    $this.deactivate($(this));
14341                });
14342                $this.deactivate(activeSibling);
14343            }
14344
14345            if($this.cfg.autoDisplay||$this.active) {
14346                if(menuitem.hasClass('ui-menuitem-active'))
14347                    $this.reactivate(menuitem);
14348                else
14349                    $this.activate(menuitem);
14350            }
14351            else {
14352                $this.highlight(menuitem);
14353            }
14354        });
14355
14356        this.rootLinks.click(function(e) {
14357            var link = $(this),
14358            menuitem = link.parent(),
14359            submenu = menuitem.children('ul.ui-menu-child');
14360
14361            $this.itemClick = true;
14362
14363            if(submenu.length === 1) {
14364                if(submenu.is(':visible')) {
14365                    $this.active = false;
14366                    $this.deactivate(menuitem);
14367                }
14368                else {
14369                    $this.active = true;
14370                    $this.highlight(menuitem);
14371                    $this.showSubmenu(menuitem, submenu);
14372                }
14373            }
14374        });
14375
14376        this.links.filter('.ui-submenu-link').click(function(e) {
14377            $this.itemClick = true;
14378            e.preventDefault();
14379        });
14380
14381        this.jq.find('ul.ui-menu-list').mouseleave(function(e) {
14382           if($this.activeitem) {
14383               $this.deactivate($this.activeitem);
14384           }
14385
14386           e.stopPropagation();
14387        });
14388    },
14389
14390    bindClickModeEvents: function() {
14391        var $this = this;
14392
14393        this.links.mouseenter(function() {
14394            var menuitem = $(this).parent();
14395
14396            if(!menuitem.hasClass('ui-menuitem-active')) {
14397                menuitem.addClass('ui-menuitem-highlight').children('a.ui-menuitem-link').addClass('ui-state-hover');
14398            }
14399        })
14400        .mouseleave(function() {
14401            var menuitem = $(this).parent();
14402
14403            if(!menuitem.hasClass('ui-menuitem-active')) {
14404                menuitem.removeClass('ui-menuitem-highlight').children('a.ui-menuitem-link').removeClass('ui-state-hover');
14405            }
14406        });
14407
14408        this.links.filter('.ui-submenu-link').on('click.tieredMenu', function(e) {
14409            var link = $(this),
14410            menuitem = link.parent(),
14411            submenu = menuitem.children('ul.ui-menu-child');
14412
14413            $this.itemClick = true;
14414
14415            var activeSibling = menuitem.siblings('.ui-menuitem-active');
14416            if(activeSibling.length) {
14417                activeSibling.find('li.ui-menuitem-active').each(function() {
14418                    $this.deactivate($(this));
14419                });
14420                $this.deactivate(activeSibling);
14421            }
14422
14423            if(submenu.length) {
14424                if(submenu.is(':visible')) {
14425                    $this.deactivate(menuitem);
14426                    menuitem.addClass('ui-menuitem-highlight').children('a.ui-menuitem-link').addClass('ui-state-hover');
14427                }
14428                else {
14429                    menuitem.addClass('ui-menuitem-active').children('a.ui-menuitem-link').removeClass('ui-state-hover').addClass('ui-state-active');
14430                    $this.showSubmenu(menuitem, submenu);
14431                }
14432            }
14433
14434            e.preventDefault();
14435        }).on('mousedown.tieredMenu', function(e) {
14436            e.stopPropagation();
14437        });
14438    },
14439
14440    bindKeyEvents: function() {
14441        //not implemented
14442    },
14443
14444    bindDocumentHandler: function() {
14445        var $this = this,
14446        clickNS = 'click.' + this.id;
14447
14448        $(document.body).off(clickNS).on(clickNS, function(e) {
14449            if($this.itemClick) {
14450                $this.itemClick = false;
14451                return;
14452            }
14453
14454            $this.reset();
14455        });
14456    },
14457
14458    deactivate: function(menuitem, animate) {
14459        this.activeitem = null;
14460        menuitem.children('a.ui-menuitem-link').removeClass('ui-state-hover ui-state-active');
14461        menuitem.removeClass('ui-menuitem-active ui-menuitem-highlight');
14462
14463        if(animate)
14464            menuitem.children('ul.ui-menu-child').fadeOut('fast');
14465        else
14466            menuitem.children('ul.ui-menu-child').hide();
14467    },
14468
14469    activate: function(menuitem) {
14470        this.highlight(menuitem);
14471
14472        var submenu = menuitem.children('ul.ui-menu-child');
14473        if(submenu.length == 1) {
14474            this.showSubmenu(menuitem, submenu);
14475        }
14476    },
14477
14478    reactivate: function(menuitem) {
14479        this.activeitem = menuitem;
14480        var submenu = menuitem.children('ul.ui-menu-child'),
14481        activeChilditem = submenu.children('li.ui-menuitem-active:first'),
14482        _self = this;
14483
14484        if(activeChilditem.length == 1) {
14485            _self.deactivate(activeChilditem);
14486        }
14487    },
14488
14489    highlight: function(menuitem) {
14490        this.activeitem = menuitem;
14491        menuitem.children('a.ui-menuitem-link').addClass('ui-state-hover');
14492        menuitem.addClass('ui-menuitem-active');
14493    },
14494
14495    showSubmenu: function(menuitem, submenu) {
14496        var pos ={
14497            my: 'left top',
14498            at: 'right top',
14499            of: menuitem,
14500            collision: 'flipfit'
14501        };
14502
14503        submenu.css('z-index', ++PrimeFaces.zindex)
14504            .show()
14505            .position(pos);
14506    },
14507
14508    reset: function() {
14509        var $this = this;
14510        this.active = false;
14511
14512        this.jq.find('li.ui-menuitem-active').each(function() {
14513            $this.deactivate($(this), true);
14514        });
14515    }
14516    
14517});
14518
14519PrimeFaces.widget.Menubar = PrimeFaces.widget.TieredMenu.extend({
14520
14521    showSubmenu: function(menuitem, submenu) {
14522        var pos = null;
14523
14524        if(menuitem.parent().hasClass('ui-menu-child')) {
14525            pos = {
14526                my: 'left top',
14527                at: 'right top',
14528                of: menuitem,
14529                collision: 'flipfit'
14530            };
14531        }
14532        else {
14533            pos = {
14534                my: 'left top',
14535                at: 'left bottom',
14536                of: menuitem,
14537                collision: 'flipfit'
14538            };
14539        }
14540
14541        //avoid queuing multiple runs
14542        if(this.timeoutId) {
14543            clearTimeout(this.timeoutId);
14544        }
14545
14546        this.timeoutId = setTimeout(function () {
14547           submenu.css('z-index', ++PrimeFaces.zindex)
14548                  .show()
14549                  .position(pos)
14550        }, this.cfg.delay);
14551    },
14552
14553    //@Override
14554    bindKeyEvents: function() {
14555        var $this = this;
14556
14557        this.keyboardTarget.on('focus.menubar', function(e) {
14558            $this.highlight($this.links.eq(0).parent());
14559        })
14560        .on('blur.menubar', function() {
14561            $this.reset();
14562        })
14563        .on('keydown.menu', function(e) {
14564            var currentitem = $this.activeitem;
14565            if(!currentitem) {
14566                return;
14567            }
14568
14569            var isRootLink = !currentitem.closest('ul').hasClass('ui-menu-child'),
14570            keyCode = $.ui.keyCode;
14571
14572            switch(e.which) {
14573                    case keyCode.LEFT:
14574                        if(isRootLink) {
14575                            var prevItem = currentitem.prevAll('.ui-menuitem:not(.ui-menubar-options):first');
14576                            if(prevItem.length) {
14577                                $this.deactivate(currentitem);
14578                                $this.highlight(prevItem);
14579                            }
14580
14581                            e.preventDefault();
14582                        }
14583                        else {
14584                            if(currentitem.hasClass('ui-menu-parent') && currentitem.children('.ui-menu-child').is(':visible')) {
14585                                $this.deactivate(currentitem);
14586                                $this.highlight(currentitem);
14587                            }
14588                            else {
14589                                var parentItem = currentitem.parent().parent();
14590                                $this.deactivate(currentitem);
14591                                $this.deactivate(parentItem);
14592                                $this.highlight(parentItem);
14593                            }
14594                        }
14595                    break;
14596
14597                    case keyCode.RIGHT:
14598                        if(isRootLink) {
14599                            var nextItem = currentitem.nextAll('.ui-menuitem:not(.ui-menubar-options):first');
14600                            if(nextItem.length) {
14601                                $this.deactivate(currentitem);
14602                                $this.highlight(nextItem);
14603                            }
14604
14605                            e.preventDefault();
14606                        }
14607                        else {
14608                            if(currentitem.hasClass('ui-menu-parent')) {
14609                                var submenu = currentitem.children('.ui-menu-child');
14610
14611                                if(submenu.is(':visible'))
14612                                    $this.highlight(submenu.children('.ui-menuitem:first'));
14613                                else
14614                                    $this.activate(currentitem);
14615                            }
14616                        }
14617                    break;
14618
14619                    case keyCode.UP:
14620                        if(!isRootLink) {
14621                            var prevItem = currentitem.prev('.ui-menuitem');
14622                            if(prevItem.length) {
14623                                $this.deactivate(currentitem);
14624                                $this.highlight(prevItem);
14625                            }
14626                        }
14627
14628                        e.preventDefault();
14629                    break;
14630
14631                    case keyCode.DOWN:
14632                        if(isRootLink) {
14633                            var submenu = currentitem.children('ul.ui-menu-child');
14634                            if(submenu.is(':visible'))
14635                                $this.highlight(submenu.children('.ui-menuitem:first'));
14636                            else
14637                                $this.activate(currentitem);
14638                        }
14639                        else {
14640                            var nextItem = currentitem.next('.ui-menuitem');
14641                            if(nextItem.length) {
14642                                $this.deactivate(currentitem);
14643                                $this.highlight(nextItem);
14644                            }
14645                        }
14646
14647                        e.preventDefault();
14648                    break;
14649
14650                    case keyCode.ENTER:
14651                        var currentLink = currentitem.children('.ui-menuitem-link');
14652                        currentLink.trigger('click');
14653                        $this.jq.blur();
14654                        PrimeFaces.utils.openLink(e, currentLink);
14655                    break;
14656
14657            }
14658        });
14659    }
14660
14661});
14662
14663PrimeFaces.widget.SlideMenu = PrimeFaces.widget.Menu.extend({
14664
14665    init: function(cfg) {
14666        this._super(cfg);
14667
14668        //elements
14669        this.submenus = this.jq.find('ul.ui-menu-list');
14670        this.wrapper = this.jq.children('div.ui-slidemenu-wrapper');
14671        this.content = this.wrapper.children('div.ui-slidemenu-content');
14672        this.rootList = this.content.children('ul.ui-menu-list');
14673        this.links = this.jq.find('a.ui-menuitem-link:not(.ui-state-disabled)');
14674        this.backward = this.wrapper.children('div.ui-slidemenu-backward');
14675        this.rendered = false;
14676
14677        //config
14678        this.stack = [];
14679        this.jqWidth = this.jq.width();
14680
14681        if(!this.jq.hasClass('ui-menu-dynamic')) {
14682
14683            if(this.jq.is(':not(:visible)')) {
14684                var hiddenParent = this.jq.closest('.ui-hidden-container'),
14685                $this = this;
14686
14687                if(hiddenParent.length) {
14688                    PrimeFaces.addDeferredRender(this.id, hiddenParent.attr('id'), function() {
14689                        return $this.render();
14690                    });
14691                }
14692            }
14693            else {
14694                this.render();
14695            }
14696        }
14697
14698        this.bindEvents();
14699    },
14700
14701    bindEvents: function() {
14702        var $this = this;
14703
14704        this.links.mouseenter(function() {
14705           $(this).addClass('ui-state-hover');
14706        })
14707        .mouseleave(function() {
14708           $(this).removeClass('ui-state-hover');
14709        })
14710        .click(function(e) {
14711            var link = $(this),
14712            submenu = link.next();
14713
14714            if(submenu.length) {
14715               $this.forward(submenu);
14716               e.preventDefault();
14717            }
14718        });
14719
14720        this.backward.click(function() {
14721            $this.back();
14722        });
14723    },
14724
14725    forward: function(submenu) {
14726        var _self = this;
14727
14728        this.push(submenu);
14729
14730        var rootLeft = -1 * (this.depth() * this.jqWidth);
14731
14732        submenu.show().css({
14733            left: this.jqWidth
14734        });
14735
14736        this.rootList.animate({
14737            left: rootLeft
14738        }, 500, 'easeInOutCirc', function() {
14739            if(_self.backward.is(':hidden')) {
14740                _self.backward.fadeIn('fast');
14741            }
14742        });
14743    },
14744
14745    back: function() {
14746        if(!this.rootList.is(':animated')) {
14747            var _self = this,
14748            last = this.pop(),
14749            depth = this.depth();
14750
14751            var rootLeft = -1 * (depth * this.jqWidth);
14752
14753            this.rootList.animate({
14754                left: rootLeft
14755            }, 500, 'easeInOutCirc', function() {
14756                if(last) {
14757                    last.hide();
14758                }
14759
14760                if(depth == 0) {
14761                    _self.backward.fadeOut('fast');
14762                }
14763            });
14764        }
14765    },
14766
14767    push: function(submenu) {
14768        this.stack.push(submenu);
14769    },
14770
14771    pop: function() {
14772        return this.stack.length !== 0 ? this.stack.pop() : null;
14773    },
14774
14775    last: function() {
14776        return this.stack[this.stack.length - 1];
14777    },
14778
14779    depth: function() {
14780        return this.stack.length;
14781    },
14782
14783    render: function() {
14784        this.submenus.width(this.jq.width());
14785        this.wrapper.height(this.rootList.outerHeight(true) + this.backward.outerHeight(true));
14786        this.content.height(this.rootList.outerHeight(true));
14787        this.rendered = true;
14788    },
14789
14790    show: function() {
14791        this.align();
14792        this.jq.css('z-index', ++PrimeFaces.zindex).show();
14793
14794        if(!this.rendered) {
14795            this.render();
14796        }
14797    }
14798});
14799
14800PrimeFaces.widget.PlainMenu = PrimeFaces.widget.Menu.extend({
14801
14802    init: function(cfg) {
14803        this._super(cfg);
14804
14805        this.menuitemLinks = this.jq.find('.ui-menuitem-link:not(.ui-state-disabled)');
14806
14807        //events
14808        this.bindEvents();
14809
14810        if(this.cfg.toggleable) {
14811            this.collapsedIds = [];
14812            this.stateKey = 'menu-' + this.id;
14813            this.restoreState();
14814        }
14815    },
14816
14817    bindEvents: function() {
14818        var $this = this;
14819
14820        this.menuitemLinks.mouseenter(function(e) {
14821            if($this.jq.is(':focus')) {
14822                $this.jq.blur();
14823            }
14824
14825            $(this).addClass('ui-state-hover');
14826        })
14827        .mouseleave(function(e) {
14828            $(this).removeClass('ui-state-hover');
14829        });
14830
14831        if(this.cfg.overlay) {
14832            this.menuitemLinks.click(function() {
14833                $this.hide();
14834            });
14835
14836            this.trigger.on('keydown.ui-menu', function(e) {
14837                var keyCode = $.ui.keyCode;
14838
14839                switch(e.which) {
14840                    case keyCode.DOWN:
14841                        $this.keyboardTarget.trigger('focus.menu');
14842                        e.preventDefault();
14843                    break;
14844
14845                    case keyCode.TAB:
14846                        if($this.jq.is(':visible')) {
14847                            $this.hide();
14848                        }
14849                    break;
14850                }
14851            });
14852        }
14853
14854        if(this.cfg.toggleable) {
14855            this.jq.find('> .ui-menu-list > .ui-widget-header').on('mouseover.menu', function() {
14856                $(this).addClass('ui-state-hover');
14857            })
14858            .on('mouseout.menu', function() {
14859                $(this).removeClass('ui-state-hover');
14860            })
14861            .on('click.menu', function(e) {
14862                var header = $(this);
14863
14864                if(header.find('> h3 > .ui-icon').hasClass('ui-icon-triangle-1-s'))
14865                    $this.collapseSubmenu(header, true);
14866                else
14867                    $this.expandSubmenu(header, true);
14868
14869                PrimeFaces.clearSelection();
14870                e.preventDefault();
14871            });
14872        }
14873
14874        this.keyboardTarget.on('focus.menu', function() {
14875            $this.menuitemLinks.eq(0).addClass('ui-state-hover');
14876        })
14877        .on('blur.menu', function() {
14878            $this.menuitemLinks.filter('.ui-state-hover').removeClass('ui-state-hover');
14879        })
14880        .on('keydown.menu', function(e) {
14881            var currentLink = $this.menuitemLinks.filter('.ui-state-hover'),
14882            keyCode = $.ui.keyCode;
14883
14884            switch(e.which) {
14885                    case keyCode.UP:
14886                        var prevItem = currentLink.parent().prevAll('.ui-menuitem:first');
14887                        if(prevItem.length) {
14888                            currentLink.removeClass('ui-state-hover');
14889                            prevItem.children('.ui-menuitem-link').addClass('ui-state-hover');
14890                        }
14891
14892                        e.preventDefault();
14893                    break;
14894
14895                    case keyCode.DOWN:
14896                        var nextItem = currentLink.parent().nextAll('.ui-menuitem:first');
14897                        if(nextItem.length) {
14898                            currentLink.removeClass('ui-state-hover');
14899                            nextItem.children('.ui-menuitem-link').addClass('ui-state-hover');
14900                        }
14901
14902                        e.preventDefault();
14903                    break;
14904
14905                    case keyCode.ENTER:
14906                        currentLink.trigger('click');
14907                        $this.jq.blur();
14908                        PrimeFaces.utils.openLink(e, currentLink);
14909                    break;
14910
14911                    case keyCode.ESCAPE:
14912                        $this.hide();
14913
14914                        if($this.cfg.overlay) {
14915                            $this.trigger.focus();
14916                        }
14917                    break;
14918
14919            }
14920        });
14921    },
14922
14923    collapseSubmenu: function(header, stateful) {
14924        var items = header.nextUntil('li.ui-widget-header');
14925
14926        header.attr('aria-expanded', false)
14927                .find('> h3 > .ui-icon').removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e');
14928
14929        items.filter('.ui-submenu-child').hide();
14930
14931        if(stateful) {
14932            this.collapsedIds.push(header.attr('id'));
14933            this.saveState();
14934        }
14935    },
14936
14937    expandSubmenu: function(header, stateful) {
14938        var items = header.nextUntil('li.ui-widget-header');
14939
14940        header.attr('aria-expanded', false)
14941                .find('> h3 > .ui-icon').removeClass('ui-icon-triangle-1-e').addClass('ui-icon-triangle-1-s');
14942
14943        items.filter('.ui-submenu-child').show();
14944
14945        if(stateful) {
14946            var id = header.attr('id');
14947            this.collapsedIds = $.grep(this.collapsedIds, function(value) {
14948                return (value !== id);
14949            });
14950            this.saveState();
14951        }
14952    },
14953
14954    saveState: function() {
14955        PrimeFaces.setCookie(this.stateKey, this.collapsedIds.join(','));
14956    },
14957
14958    restoreState: function() {
14959        var collapsedIdsAsString = PrimeFaces.getCookie(this.stateKey);
14960
14961        if(collapsedIdsAsString) {
14962            this.collapsedIds = collapsedIdsAsString.split(',');
14963
14964            for(var i = 0 ; i < this.collapsedIds.length; i++) {
14965                this.collapseSubmenu($(PrimeFaces.escapeClientId(this.collapsedIds[i])), false);
14966            }
14967        }
14968    },
14969
14970    clearState: function() {
14971        PrimeFaces.setCookie(this.stateKey, null);
14972    }
14973
14974});
14975
14976PrimeFaces.widget.MenuButton = PrimeFaces.widget.BaseWidget.extend({
14977
14978    init: function(cfg) {
14979        this._super(cfg);
14980
14981        this.menuId = this.jqId + '_menu';
14982        this.button = this.jq.children('button');
14983        this.menu = this.jq.children('.ui-menu');
14984        this.menuitems = this.jq.find('.ui-menuitem');
14985        this.cfg.disabled = this.button.is(':disabled');
14986
14987        if(!this.cfg.disabled) {
14988            this.bindEvents();
14989            PrimeFaces.utils.registerDynamicOverlay(this, this.menu, this.id + '_menu');
14990        }
14991    },
14992
14993    //@override
14994    refresh: function(cfg) {
14995        this._super(cfg);
14996    },
14997
14998    bindEvents: function() {
14999        var $this = this;
15000
15001        //button visuals
15002        this.button.mouseover(function(){
15003            if(!$this.button.hasClass('ui-state-focus')) {
15004                $this.button.addClass('ui-state-hover');
15005            }
15006        }).mouseout(function() {
15007            if(!$this.button.hasClass('ui-state-focus')) {
15008                $this.button.removeClass('ui-state-hover ui-state-active');
15009            }
15010        }).mousedown(function() {
15011            $(this).removeClass('ui-state-focus ui-state-hover').addClass('ui-state-active');
15012        }).mouseup(function() {
15013            var el = $(this);
15014            el.removeClass('ui-state-active')
15015
15016            if($this.menu.is(':visible')) {
15017                el.addClass('ui-state-hover');
15018                $this.hide();
15019            }
15020            else {
15021                el.addClass('ui-state-focus');
15022                $this.show();
15023            }
15024        }).focus(function() {
15025            $(this).addClass('ui-state-focus');
15026        }).blur(function() {
15027            $(this).removeClass('ui-state-focus');
15028        });
15029
15030        //mark button and descandants of button as a trigger for a primefaces overlay
15031        this.button.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
15032
15033        //menuitem visuals
15034        this.menuitems.mouseover(function(e) {
15035            var element = $(this);
15036            if(!element.hasClass('ui-state-disabled')) {
15037                element.addClass('ui-state-hover');
15038            }
15039        }).mouseout(function(e) {
15040            $(this).removeClass('ui-state-hover');
15041        }).click(function() {
15042            $this.button.removeClass('ui-state-focus');
15043            $this.hide();
15044        });
15045
15046        //keyboard support
15047        this.button.keydown(function(e) {
15048            var keyCode = $.ui.keyCode;
15049
15050            switch(e.which) {
15051                case keyCode.UP:
15052                    if($this.menu.is(':visible')) {
15053                        var highlightedItem = $this.menuitems.filter('.ui-state-hover'),
15054                        prevItems = highlightedItem.length ? highlightedItem.prevAll(':not(.ui-separator)') : null;
15055
15056                        if(prevItems && prevItems.length) {
15057                            highlightedItem.removeClass('ui-state-hover');
15058                            prevItems.eq(0).addClass('ui-state-hover');
15059                        }
15060                    }
15061                    e.preventDefault();
15062                break;
15063
15064                case keyCode.DOWN:
15065                    if($this.menu.is(':visible')) {
15066                        var highlightedItem = $this.menuitems.filter('.ui-state-hover'),
15067                        nextItems = highlightedItem.length ? highlightedItem.nextAll(':not(.ui-separator)') : $this.menuitems.eq(0);
15068
15069                        if(nextItems.length) {
15070                            highlightedItem.removeClass('ui-state-hover');
15071                            nextItems.eq(0).addClass('ui-state-hover');
15072                        }
15073                    }
15074                    e.preventDefault();
15075                break;
15076
15077                case keyCode.ENTER:
15078                case keyCode.SPACE:
15079                    if($this.menu.is(':visible'))
15080                        $this.menuitems.filter('.ui-state-hover').children('a').trigger('click');
15081                    else
15082                        $this.show();
15083
15084                    e.preventDefault();
15085                break;
15086
15087
15088                case keyCode.ESCAPE:
15089                case keyCode.TAB:
15090                    $this.hide();
15091                break;
15092            }
15093        });
15094
15095        if (!$this.cfg.disabled) {
15096            PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.menu,
15097                function() { return $this.button; },
15098                function(e, eventTarget) {
15099                    if(!($this.menu.is(eventTarget) || $this.menu.has(eventTarget).length > 0)) {
15100                        $this.button.removeClass('ui-state-focus ui-state-hover');
15101                        $this.hide();
15102                    }
15103                });
15104        }
15105
15106        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.menu, function() {
15107            $this.alignPanel();
15108        });
15109
15110        //aria
15111        this.button.attr('role', 'button').attr('aria-disabled', this.button.is(':disabled'));
15112    },
15113
15114    show: function() {
15115        this.alignPanel();
15116
15117        this.menu.show();
15118    },
15119
15120    hide: function() {
15121        this.menuitems.filter('.ui-state-hover').removeClass('ui-state-hover');
15122
15123        this.menu.fadeOut('fast');
15124    },
15125
15126    alignPanel: function() {
15127        this.menu.css({left:'', top:'','z-index': ++PrimeFaces.zindex});
15128
15129        if(this.menu.parent().is(this.jq)) {
15130            this.menu.css({
15131                left: 0,
15132                top: this.jq.innerHeight()
15133            });
15134        }
15135        else {
15136            this.menu.position({
15137                my: 'left top',
15138                at: 'left bottom',
15139                of: this.button,
15140                collision: this.cfg.collision || "flip"
15141            });
15142        }
15143    }
15144
15145});
15146
15147PrimeFaces.widget.ContextMenu = PrimeFaces.widget.TieredMenu.extend({
15148
15149    init: function(cfg) {
15150        cfg.autoDisplay = true;
15151        this._super(cfg);
15152        this.cfg.selectionMode = this.cfg.selectionMode||'multiple';
15153
15154        var $this = this,
15155        documentTarget = (this.cfg.target === undefined);
15156
15157        //event
15158        this.cfg.event = this.cfg.event||'contextmenu';
15159
15160        //target
15161        this.jqTargetId = documentTarget ? document : PrimeFaces.escapeClientId(this.cfg.target);
15162        this.jqTarget = $(this.jqTargetId);
15163
15164        //append to body
15165        this.cfg.appendTo = '@(body)';
15166        PrimeFaces.utils.registerDynamicOverlay(this, this.jq, this.id);
15167
15168        //attach contextmenu
15169        if(documentTarget) {
15170            var event = 'contextmenu.' + this.id + '_contextmenu';
15171            
15172            $(document).off(event).on(event, function(e) {
15173                $this.show(e);
15174            });
15175
15176            if (PrimeFaces.env.touch) {
15177                $(document).swipe({
15178                    longTap:function(e, target) {
15179                       $this.show(e);
15180                    }
15181                });
15182            }
15183        }
15184        else {
15185            var binded = false;
15186
15187            if (this.cfg.targetWidgetVar) {
15188                var targetWidget = PrimeFaces.widgets[this.cfg.targetWidgetVar];
15189
15190                if (targetWidget) {
15191                    if (typeof targetWidget.bindContextMenu === 'function') {
15192                        targetWidget.bindContextMenu(this, targetWidget, this.jqTargetId, this.cfg);
15193                        binded = true;
15194                    }
15195                }
15196                else {
15197                    PrimeFaces.warn("ContextMenu targets a widget which is not available yet. Please place the contextMenu after the target component. targetWidgetVar: " + this.cfg.targetWidgetVar);
15198                }
15199            }
15200
15201            if (binded === false) {
15202                var event = this.cfg.event + '.' + this.id + '_contextmenu';
15203
15204                $(document).off(event, this.jqTargetId).on(event, this.jqTargetId, null, function(e) {
15205                    $this.show(e);
15206                });
15207
15208                if (PrimeFaces.env.touch) {
15209                    $(this.jqTargetId).swipe({
15210                        longTap:function(e, target) {
15211                           $this.show(e);
15212                        }
15213                    });
15214                }
15215            }
15216        }
15217
15218
15219        PrimeFaces.utils.registerHideOverlayHandler(this, 'click.' + this.id + '_hide', this.jq,
15220            function(e) { return e.which == 3 ? $this.jqTarget : null; },
15221            function(e, eventTarget) {
15222                if(!($this.jq.is(eventTarget) || $this.jq.has(eventTarget).length > 0)) {
15223                    $this.hide();
15224                }
15225            });
15226
15227        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', this.jq, function() {
15228            $this.hide();
15229        });
15230    },
15231
15232    // @Override
15233    bindItemEvents: function() {
15234        this._super();
15235
15236        var $this = this;
15237
15238        //hide menu on item click
15239        this.links.on('click', function(e) {
15240            var target = $(e.target),
15241                submenuLink = target.hasClass('ui-submenu-link') ? target : target.closest('.ui-submenu-link');
15242
15243            if (submenuLink.length) {
15244                return;
15245            }
15246
15247            $this.hide();
15248        });
15249    },
15250
15251    show: function(e) {
15252        if(this.cfg.targetFilter && $(e.target).is(':not(' + this.cfg.targetFilter + ')')) {
15253            return;
15254        }
15255
15256        //hide other contextmenus if any
15257        $(document.body).children('.ui-contextmenu:visible').hide();
15258
15259        if(this.cfg.beforeShow) {
15260            var retVal = this.cfg.beforeShow.call(this, e);
15261            if(retVal === false) {
15262                return;
15263            }
15264        }
15265
15266        var win = $(window),
15267        left = e.pageX,
15268        top = e.pageY,
15269        width = this.jq.outerWidth(),
15270        height = this.jq.outerHeight();
15271
15272        //collision detection for window boundaries
15273        if((left + width) > (win.width())+ win.scrollLeft()) {
15274            left = left - width;
15275        }
15276        if((top + height ) > (win.height() + win.scrollTop())) {
15277            top = top - height;
15278        }
15279        if(top < 0) {
15280            top = e.pageY;
15281        }
15282
15283        this.jq.css({
15284            'left': left,
15285            'top': top,
15286            'z-index': ++PrimeFaces.zindex
15287        }).show();
15288
15289        e.preventDefault();
15290        e.stopPropagation();
15291    },
15292
15293    hide: function() {
15294        var $this = this;
15295
15296        //hide submenus
15297        this.jq.find('li.ui-menuitem-active').each(function() {
15298            $this.deactivate($(this), true);
15299        });
15300
15301        this.jq.fadeOut('fast');
15302    },
15303
15304    isVisible: function() {
15305        return this.jq.is(':visible');
15306    },
15307
15308    getTarget: function() {
15309        return this.jqTarget;
15310    }
15311
15312});
15313
15314PrimeFaces.widget.MegaMenu = PrimeFaces.widget.BaseWidget.extend({
15315
15316    init: function(cfg) {
15317        this._super(cfg);
15318
15319        this.cfg.vertical = this.jq.hasClass('ui-megamenu-vertical');
15320        this.rootList = this.jq.children('ul.ui-menu-list');
15321        this.rootLinks = this.rootList.find('> li.ui-menuitem > a.ui-menuitem-link:not(.ui-state-disabled)');
15322        this.subLinks = this.jq.find('.ui-menu-child a.ui-menuitem-link:not(.ui-state-disabled)');
15323        this.keyboardTarget = this.jq.children('.ui-helper-hidden-accessible');
15324
15325        if(this.cfg.activeIndex !== undefined) {
15326            this.rootLinks.eq(this.cfg.activeIndex).addClass('ui-state-hover').closest('li.ui-menuitem').addClass('ui-menuitem-active');
15327        }
15328
15329        this.bindEvents();
15330        this.bindKeyEvents();
15331    },
15332
15333    bindEvents: function() {
15334        var $this = this;
15335
15336        this.rootLinks.mouseenter(function(e) {
15337            var link = $(this),
15338            menuitem = link.parent();
15339
15340            var current = menuitem.siblings('.ui-menuitem-active');
15341            if(current.length > 0) {
15342                current.find('li.ui-menuitem-active').each(function() {
15343                    $this.deactivate($(this));
15344                });
15345                $this.deactivate(current, false);
15346            }
15347
15348            if($this.cfg.autoDisplay||$this.active) {
15349                $this.activate(menuitem);
15350            }
15351            else {
15352                $this.highlight(menuitem);
15353            }
15354
15355        });
15356
15357        if(this.cfg.autoDisplay === false) {
15358            this.rootLinks.data('primefaces-megamenu', this.id).find('*').data('primefaces-megamenu', this.id)
15359
15360            this.rootLinks.click(function(e) {
15361                var link = $(this),
15362                menuitem = link.parent(),
15363                submenu = link.next();
15364
15365                if(submenu.length === 1) {
15366                    if(submenu.is(':visible')) {
15367                        $this.active = false;
15368                        $this.deactivate(menuitem, true);
15369                    }
15370                    else {
15371                        $this.active = true;
15372                        $this.activate(menuitem);
15373                    }
15374                }
15375                else {
15376                    PrimeFaces.utils.openLink(e, link);
15377                }
15378
15379                e.preventDefault();
15380            });
15381        }
15382        else {
15383            this.rootLinks.filter('.ui-submenu-link').click(function(e) {
15384                e.preventDefault();
15385            });
15386        }
15387
15388        this.subLinks.mouseenter(function() {
15389            if($this.activeitem && !$this.isRootLink($this.activeitem)) {
15390                $this.deactivate($this.activeitem);
15391            }
15392            $this.highlight($(this).parent());
15393        })
15394        .mouseleave(function() {
15395            if($this.activeitem && !$this.isRootLink($this.activeitem)) {
15396                $this.deactivate($this.activeitem);
15397            }
15398            $(this).removeClass('ui-state-hover');
15399        });
15400
15401        this.rootList.mouseleave(function(e) {
15402            var activeitem = $this.rootList.children('.ui-menuitem-active');
15403            if(activeitem.length === 1) {
15404                $this.deactivate(activeitem, false);
15405            }
15406        });
15407
15408        this.rootList.find('> li.ui-menuitem > ul.ui-menu-child').mouseleave(function(e) {
15409            e.stopPropagation();
15410        });
15411
15412        $(document.body).click(function(e) {
15413            var target = $(e.target);
15414            if(target.data('primefaces-megamenu') === $this.id) {
15415                return;
15416            }
15417
15418            $this.active = false;
15419            $this.deactivate($this.rootList.children('li.ui-menuitem-active'), true);
15420        });
15421    },
15422
15423    bindKeyEvents: function() {
15424        var $this = this;
15425
15426        this.keyboardTarget.on('focus.megamenu', function(e) {
15427            $this.highlight($this.rootLinks.eq(0).parent());
15428        })
15429        .on('blur.megamenu', function() {
15430            $this.reset();
15431        })
15432        .on('keydown.megamenu', function(e) {
15433            var currentitem = $this.activeitem;
15434            if(!currentitem) {
15435                return;
15436            }
15437
15438            var isRootLink = $this.isRootLink(currentitem),
15439            keyCode = $.ui.keyCode;
15440
15441            switch(e.which) {
15442                    case keyCode.LEFT:
15443                        if(isRootLink && !$this.cfg.vertical) {
15444                            var prevItem = currentitem.prevAll('.ui-menuitem:first');
15445                            if(prevItem.length) {
15446                                $this.deactivate(currentitem);
15447                                $this.highlight(prevItem);
15448                            }
15449
15450                            e.preventDefault();
15451                        }
15452                        else {
15453                            if(currentitem.hasClass('ui-menu-parent') && currentitem.children('.ui-menu-child').is(':visible')) {
15454                                $this.deactivate(currentitem);
15455                                $this.highlight(currentitem);
15456                            }
15457                            else {
15458                                var parentItem = currentitem.closest('ul.ui-menu-child').parent();
15459                                if(parentItem.length) {
15460                                    $this.deactivate(currentitem);
15461                                    $this.deactivate(parentItem);
15462                                    $this.highlight(parentItem);
15463                                }
15464                            }
15465                        }
15466                    break;
15467
15468                    case keyCode.RIGHT:
15469                        if(isRootLink && !$this.cfg.vertical) {
15470                            var nextItem = currentitem.nextAll('.ui-menuitem:visible:first');
15471                            if(nextItem.length) {
15472                                $this.deactivate(currentitem);
15473                                $this.highlight(nextItem);
15474                            }
15475
15476                            e.preventDefault();
15477                        }
15478                        else {
15479
15480                            if(currentitem.hasClass('ui-menu-parent')) {
15481                                var submenu = currentitem.children('.ui-menu-child');
15482                                if(submenu.is(':visible')) {
15483                                    $this.highlight(submenu.find('ul.ui-menu-list:visible > .ui-menuitem:visible:first'));
15484                                }
15485                                else {
15486                                    $this.activate(currentitem);
15487                                }
15488                            }
15489                        }
15490                    break;
15491
15492                    case keyCode.UP:
15493                        if(!isRootLink || $this.cfg.vertical) {
15494                            var prevItem = $this.findPrevItem(currentitem);
15495                            if(prevItem.length) {
15496                                $this.deactivate(currentitem);
15497                                $this.highlight(prevItem);
15498                            }
15499                        }
15500
15501                        e.preventDefault();
15502                    break;
15503
15504                    case keyCode.DOWN:
15505                        if(isRootLink && !$this.cfg.vertical) {
15506                            var submenu = currentitem.children('ul.ui-menu-child');
15507                            if(submenu.is(':visible')) {
15508                                var firstMenulist = $this.getFirstMenuList(submenu);
15509                                $this.highlight(firstMenulist.children('.ui-menuitem:visible:first'));
15510                            }
15511                            else {
15512                                $this.activate(currentitem);
15513                            }
15514                        }
15515                        else {
15516                            var nextItem = $this.findNextItem(currentitem);
15517                            if(nextItem.length) {
15518                                $this.deactivate(currentitem);
15519                                $this.highlight(nextItem);
15520                            }
15521                        }
15522
15523                        e.preventDefault();
15524                    break;
15525
15526                    case keyCode.ENTER:
15527                        var currentLink = currentitem.children('.ui-menuitem-link');
15528                        currentLink.trigger('click');
15529                        $this.jq.blur();
15530                        var href = currentLink.attr('href');
15531                        if(href && href !== '#') {
15532                            window.location.href = href;
15533                        }
15534                        $this.deactivate(currentitem);
15535                        e.preventDefault();
15536                    break;
15537
15538                    case keyCode.ESCAPE:
15539                        if(currentitem.hasClass('ui-menu-parent')) {
15540                            var submenu = currentitem.children('ul.ui-menu-list:visible');
15541                            if(submenu.length > 0) {
15542                                submenu.hide();
15543                            }
15544                        }
15545                        else {
15546                            var parentItem = currentitem.closest('ul.ui-menu-child').parent();
15547                            if(parentItem.length) {
15548                                $this.deactivate(currentitem);
15549                                $this.deactivate(parentItem);
15550                                $this.highlight(parentItem);
15551                            }
15552                        }
15553                        e.preventDefault();
15554                    break;
15555            }
15556        });
15557    },
15558
15559    findPrevItem: function(menuitem) {
15560        var previtem = menuitem.prev('.ui-menuitem');
15561
15562        if(!previtem.length) {
15563            var prevSubmenu = menuitem.closest('ul.ui-menu-list').prev('.ui-menu-list');
15564
15565            if(!prevSubmenu.length) {
15566                prevSubmenu = menuitem.closest('td').prev('td').children('.ui-menu-list:visible:last');
15567            }
15568
15569            if(prevSubmenu.length) {
15570                previtem = prevSubmenu.find('li.ui-menuitem:visible:last');
15571            }
15572        }
15573        return previtem;
15574    },
15575
15576    findNextItem: function(menuitem) {
15577        var nextitem = menuitem.next('.ui-menuitem');
15578
15579        if(!nextitem.length) {
15580            var nextSubmenu = menuitem.closest('ul.ui-menu-list').next('.ui-menu-list');
15581            if(!nextSubmenu.length) {
15582                nextSubmenu = menuitem.closest('td').next('td').children('.ui-menu-list:visible:first');
15583            }
15584
15585            if(nextSubmenu.length) {
15586                nextitem = nextSubmenu.find('li.ui-menuitem:visible:first');
15587            }
15588        }
15589        return nextitem;
15590    },
15591
15592    getFirstMenuList: function(submenu) {
15593        return submenu.find('.ui-menu-list:not(.ui-state-disabled):first');
15594    },
15595
15596    isRootLink: function(menuitem) {
15597        var submenu = menuitem.closest('ul');
15598        return submenu.parent().hasClass('ui-menu');
15599    },
15600
15601    reset: function() {
15602        var $this = this;
15603        this.active = false;
15604
15605        this.jq.find('li.ui-menuitem-active').each(function() {
15606            $this.deactivate($(this), true);
15607        });
15608    },
15609
15610    deactivate: function(menuitem, animate) {
15611        var link = menuitem.children('a.ui-menuitem-link'),
15612        submenu = link.next();
15613
15614        menuitem.removeClass('ui-menuitem-active');
15615        link.removeClass('ui-state-hover');
15616        this.activeitem = null;
15617
15618        if(submenu.length > 0) {
15619            if(animate)
15620                submenu.fadeOut('fast');
15621            else
15622                submenu.hide();
15623        }
15624    },
15625
15626    highlight: function(menuitem) {
15627        var link = menuitem.children('a.ui-menuitem-link');
15628
15629        menuitem.addClass('ui-menuitem-active');
15630        link.addClass('ui-state-hover');
15631        this.activeitem = menuitem;
15632    },
15633
15634    activate: function(menuitem) {
15635        var submenu = menuitem.children('.ui-menu-child'),
15636        $this = this;
15637
15638        $this.highlight(menuitem);
15639
15640        if(submenu.length > 0) {
15641            $this.showSubmenu(menuitem, submenu);
15642        }
15643    },
15644
15645    showSubmenu: function(menuitem, submenu) {
15646        var pos = null;
15647
15648        if(this.cfg.vertical) {
15649            pos = {
15650                my: 'left top',
15651                at: 'right top',
15652                of: menuitem,
15653                collision: 'flipfit'
15654            };
15655        }
15656        else {
15657            pos = {
15658                my: 'left top',
15659                at: 'left bottom',
15660                of: menuitem,
15661                collision: 'flipfit'
15662            };
15663        }
15664
15665        //avoid queuing multiple runs
15666        if(this.timeoutId) {
15667            clearTimeout(this.timeoutId);
15668        }
15669
15670        this.timeoutId = setTimeout(function () {
15671           submenu.css('z-index', ++PrimeFaces.zindex)
15672                  .show()
15673                  .position(pos)
15674        }, this.cfg.delay);
15675    }
15676
15677});
15678
15679PrimeFaces.widget.PanelMenu = PrimeFaces.widget.BaseWidget.extend({
15680
15681    init: function(cfg) {
15682        this._super(cfg);
15683        this.headers = this.jq.find('> .ui-panelmenu-panel > h3.ui-panelmenu-header:not(.ui-state-disabled)');
15684        this.menuContent = this.jq.find('> .ui-panelmenu-panel > .ui-panelmenu-content');
15685        this.menuitemLinks = this.menuContent.find('.ui-menuitem-link:not(.ui-state-disabled)');
15686        this.menuText = this.menuitemLinks.find('.ui-menuitem-text');
15687        this.treeLinks = this.menuContent.find('.ui-menu-parent > .ui-menuitem-link:not(.ui-state-disabled)');
15688
15689        //keyboard support
15690        this.focusedItem = null;
15691        this.menuText.attr('tabindex', -1);
15692
15693        //ScreenReader support
15694        this.menuText.attr('role', 'menuitem');
15695        this.treeLinks.find('> .ui-menuitem-text').attr('aria-expanded', false);
15696
15697        this.bindEvents();
15698
15699        if(this.cfg.stateful) {
15700            this.stateKey = 'panelMenu-' + this.id;
15701        }
15702
15703        this.restoreState();
15704    },
15705
15706    bindEvents: function() {
15707        var $this = this;
15708
15709        this.headers.mouseover(function() {
15710            var element = $(this);
15711            if(!element.hasClass('ui-state-active')) {
15712                element.addClass('ui-state-hover');
15713            }
15714        }).mouseout(function() {
15715            var element = $(this);
15716            if(!element.hasClass('ui-state-active')) {
15717                element.removeClass('ui-state-hover');
15718            }
15719        }).click(function(e) {
15720            var header = $(this);
15721
15722            if (!$this.cfg.multiple) {
15723                $this.collapseActiveSibling(header);
15724            }
15725
15726            if (header.hasClass('ui-state-active'))
15727                $this.collapseRootSubmenu($(this));
15728            else
15729                $this.expandRootSubmenu($(this), false);
15730
15731            $this.removeFocusedItem();
15732            header.focus();
15733            e.preventDefault();
15734        });
15735
15736        this.menuitemLinks.mouseover(function() {
15737            $(this).addClass('ui-state-hover');
15738        }).mouseout(function() {
15739            $(this).removeClass('ui-state-hover');
15740        }).click(function(e) {
15741            var currentLink = $(this);
15742            $this.focusItem(currentLink.closest('.ui-menuitem'));
15743            PrimeFaces.utils.openLink(e, currentLink);
15744        });
15745
15746        this.treeLinks.click(function(e) {
15747            var link = $(this),
15748            submenu = link.parent(),
15749            submenuList = link.next();
15750
15751            if(submenuList.is(':visible'))
15752                $this.collapseTreeItem(submenu);
15753            else
15754                $this.expandTreeItem(submenu, false);
15755
15756            e.preventDefault();
15757        });
15758
15759        this.bindKeyEvents();
15760    },
15761
15762    bindKeyEvents: function() {
15763        var $this = this;
15764
15765        if(PrimeFaces.env.isIE()) {
15766            this.focusCheck = false;
15767        }
15768
15769        this.headers.on('focus.panelmenu', function(){
15770            $(this).addClass('ui-menuitem-outline');
15771        })
15772        .on('blur.panelmenu', function(){
15773            $(this).removeClass('ui-menuitem-outline ui-state-hover');
15774        })
15775        .on('keydown.panelmenu', function(e) {
15776            var keyCode = $.ui.keyCode,
15777            key = e.which;
15778
15779            if(key === keyCode.SPACE || key === keyCode.ENTER) {
15780                $(this).trigger('click');
15781                e.preventDefault();
15782            }
15783        });
15784
15785        this.menuContent.on('mousedown.panelmenu', function(e) {
15786            if($(e.target).is(':not(:input:enabled)')) {
15787                e.preventDefault();
15788            }
15789        }).on('focus.panelmenu', function(){
15790            if(!$this.focusedItem) {
15791                $this.focusItem($this.getFirstItemOfContent($(this)));
15792                if(PrimeFaces.env.isIE()) {
15793                    $this.focusCheck = false;
15794                }
15795            }
15796        });
15797
15798        this.menuContent.off('keydown.panelmenu blur.panelmenu').on('keydown.panelmenu', function(e) {
15799            if(!$this.focusedItem) {
15800                return;
15801            }
15802
15803            var keyCode = $.ui.keyCode;
15804
15805            switch(e.which) {
15806                case keyCode.LEFT:
15807                    if($this.isExpanded($this.focusedItem)) {
15808                        $this.focusedItem.children('.ui-menuitem-link').trigger('click');
15809                    }
15810                    else {
15811                        var parentListOfItem = $this.focusedItem.closest('ul.ui-menu-list');
15812
15813                        if(parentListOfItem.parent().is(':not(.ui-panelmenu-content)')) {
15814                            $this.focusItem(parentListOfItem.closest('li.ui-menuitem'));
15815                        }
15816                    }
15817
15818                    e.preventDefault();
15819                break;
15820
15821                case keyCode.RIGHT:
15822                    if($this.focusedItem.hasClass('ui-menu-parent') && !$this.isExpanded($this.focusedItem)) {
15823                        $this.focusedItem.children('.ui-menuitem-link').trigger('click');
15824                    }
15825                    e.preventDefault();
15826                break;
15827
15828                case keyCode.UP:
15829                    var itemToFocus = null,
15830                    prevItem = $this.focusedItem.prev();
15831
15832                    if(prevItem.length) {
15833                        itemToFocus = prevItem.find('li.ui-menuitem:visible:last');
15834                        if(!itemToFocus.length) {
15835                            itemToFocus = prevItem;
15836                        }
15837                    }
15838                    else {
15839                        itemToFocus = $this.focusedItem.closest('ul').parent('li');
15840                    }
15841
15842                    if(itemToFocus.length) {
15843                        $this.focusItem(itemToFocus);
15844                    }
15845
15846                    e.preventDefault();
15847                break;
15848
15849                case keyCode.DOWN:
15850                    var itemToFocus = null,
15851                    firstVisibleChildItem = $this.focusedItem.find('> ul > li:visible:first');
15852
15853                    if(firstVisibleChildItem.length) {
15854                        itemToFocus = firstVisibleChildItem;
15855                    }
15856                    else if($this.focusedItem.next().length) {
15857                        itemToFocus = $this.focusedItem.next();
15858                    }
15859                    else {
15860                        if($this.focusedItem.next().length === 0) {
15861                            itemToFocus = $this.searchDown($this.focusedItem);
15862                        }
15863                    }
15864
15865                    if(itemToFocus && itemToFocus.length) {
15866                        $this.focusItem(itemToFocus);
15867                    }
15868
15869                    e.preventDefault();
15870                break;
15871
15872                case keyCode.ENTER:
15873                case keyCode.SPACE:
15874                    var currentLink = $this.focusedItem.children('.ui-menuitem-link');
15875                    //IE fix
15876                    setTimeout(function(){
15877                        currentLink.trigger('click');
15878                    },1);
15879                    $this.jq.blur();
15880
15881                    var href = currentLink.attr('href');
15882                    if(href && href !== '#') {
15883                        window.location.href = href;
15884                    }
15885                    e.preventDefault();
15886                break;
15887
15888                case keyCode.TAB:
15889                    if($this.focusedItem) {
15890                        if(PrimeFaces.env.isIE()) {
15891                            $this.focusCheck = true;
15892                        }
15893                        $(this).focus();
15894                    }
15895                break;
15896            }
15897        }).on('blur.panelmenu', function(e) {
15898            if(PrimeFaces.env.isIE() && !$this.focusCheck) {
15899                return;
15900            }
15901
15902            $this.removeFocusedItem();
15903        });
15904
15905        var clickNS = 'click.' + this.id;
15906        //remove focusedItem when document is clicked
15907        $(document.body).off(clickNS).on(clickNS, function(event) {
15908            if(!$(event.target).closest('.ui-panelmenu').length) {
15909               $this.removeFocusedItem();
15910            }
15911        });
15912    },
15913
15914    collapseActiveSibling: function(header) {
15915        this.collapseRootSubmenu(header.parent().siblings().children('.ui-panelmenu-header.ui-state-active').eq(0));
15916    },
15917
15918    searchDown: function(item) {
15919        var nextOfParent = item.closest('ul').parent('li').next(),
15920        itemToFocus = null;
15921
15922        if(nextOfParent.length) {
15923            itemToFocus = nextOfParent;
15924        }
15925        else if(item.closest('ul').parent('li').length === 0){
15926            itemToFocus = item;
15927        }
15928        else {
15929            itemToFocus = this.searchDown(item.closest('ul').parent('li'));
15930        }
15931
15932        return itemToFocus;
15933    },
15934
15935    getFirstItemOfContent: function(content) {
15936        return content.find('> .ui-menu-list > .ui-menuitem:visible:first-child');
15937    },
15938
15939    getItemText: function(item) {
15940        return item.find('> .ui-menuitem-link > span.ui-menuitem-text');
15941    },
15942
15943    focusItem: function(item) {
15944        this.removeFocusedItem();
15945        this.getItemText(item).addClass('ui-menuitem-outline').focus();
15946        this.focusedItem = item;
15947    },
15948
15949    removeFocusedItem: function() {
15950        if(this.focusedItem) {
15951            this.getItemText(this.focusedItem).removeClass('ui-menuitem-outline');
15952            this.focusedItem = null;
15953        }
15954    },
15955
15956    isExpanded: function(item) {
15957        return item.children('ul.ui-menu-list').is(':visible');
15958    },
15959
15960    collapseRootSubmenu: function(header) {
15961        var panel = header.next();
15962
15963        header.attr('aria-expanded', false).removeClass('ui-state-active ui-corner-top').addClass('ui-state-hover ui-corner-all')
15964                            .children('.ui-icon').removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e');
15965
15966        panel.attr('aria-hidden', true).slideUp('normal', 'easeInOutCirc');
15967
15968        this.removeAsExpanded(panel);
15969    },
15970
15971    expandRootSubmenu: function(header, restoring) {
15972        var panel = header.next();
15973
15974        header.attr('aria-expanded', true).addClass('ui-state-active ui-corner-top').removeClass('ui-state-hover ui-corner-all')
15975                .children('.ui-icon').removeClass('ui-icon-triangle-1-e').addClass('ui-icon-triangle-1-s');
15976
15977        if(restoring) {
15978            panel.attr('aria-hidden', false).show();
15979        }
15980        else {
15981            panel.attr('aria-hidden', false).slideDown('normal', 'easeInOutCirc');
15982
15983            this.addAsExpanded(panel);
15984        }
15985    },
15986
15987    expandTreeItem: function(submenu, restoring) {
15988        var submenuLink = submenu.find('> .ui-menuitem-link');
15989
15990        submenuLink.find('> .ui-menuitem-text').attr('aria-expanded', true);
15991        submenuLink.find('> .ui-panelmenu-icon').addClass('ui-icon-triangle-1-s');
15992        submenu.children('.ui-menu-list').show();
15993
15994        if(!restoring) {
15995            this.addAsExpanded(submenu);
15996        }
15997    },
15998
15999    collapseTreeItem: function(submenu) {
16000        var submenuLink = submenu.find('> .ui-menuitem-link');
16001
16002        submenuLink.find('> .ui-menuitem-text').attr('aria-expanded', false);
16003        submenuLink.find('> .ui-panelmenu-icon').removeClass('ui-icon-triangle-1-s');
16004        submenu.children('.ui-menu-list').hide();
16005
16006        this.removeAsExpanded(submenu);
16007    },
16008
16009    saveState: function() {
16010        if(this.cfg.stateful) {
16011            var expandedNodeIds = this.expandedNodes.join(',');
16012
16013            PrimeFaces.setCookie(this.stateKey, expandedNodeIds, {path:'/'});
16014        }
16015    },
16016
16017    restoreState: function() {
16018        var expandedNodeIds = null;
16019
16020        if(this.cfg.stateful) {
16021            expandedNodeIds = PrimeFaces.getCookie(this.stateKey);
16022        }
16023
16024        if(expandedNodeIds) {
16025            this.collapseAll();
16026            this.expandedNodes = expandedNodeIds.split(',');
16027
16028            for(var i = 0 ; i < this.expandedNodes.length; i++) {
16029                var element = $(PrimeFaces.escapeClientId(this.expandedNodes[i]));
16030                if(element.is('div.ui-panelmenu-content'))
16031                    this.expandRootSubmenu(element.prev(), true);
16032                else if(element.is('li.ui-menu-parent'))
16033                    this.expandTreeItem(element, true);
16034            }
16035        }
16036        else {
16037            this.expandedNodes = [];
16038            var activeHeaders = this.headers.filter('.ui-state-active'),
16039            activeTreeSubmenus = this.jq.find('.ui-menu-parent > .ui-menu-list:not(.ui-helper-hidden)');
16040
16041            for(var i = 0; i < activeHeaders.length; i++) {
16042                this.expandedNodes.push(activeHeaders.eq(i).next().attr('id'));
16043            }
16044
16045            for(var i = 0; i < activeTreeSubmenus.length; i++) {
16046                this.expandedNodes.push(activeTreeSubmenus.eq(i).parent().attr('id'));
16047            }
16048        }
16049    },
16050
16051    removeAsExpanded: function(element) {
16052        var id = element.attr('id');
16053
16054        this.expandedNodes = $.grep(this.expandedNodes, function(value) {
16055            return value != id;
16056        });
16057
16058        this.saveState();
16059    },
16060
16061    addAsExpanded: function(element) {
16062        this.expandedNodes.push(element.attr('id'));
16063
16064        this.saveState();
16065    },
16066
16067    clearState: function() {
16068        if(this.cfg.stateful) {
16069            PrimeFaces.deleteCookie(this.stateKey, {path:'/'});
16070        }
16071    },
16072
16073    collapseAll: function() {
16074        this.headers.filter('.ui-state-active').each(function() {
16075            var header = $(this);
16076            header.removeClass('ui-state-active').children('.ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e').removeClass('ui-icon-triangle-1-s');
16077            header.next().addClass('ui-helper-hidden');
16078        });
16079
16080        this.jq.find('.ui-menu-parent > .ui-menu-list:not(.ui-helper-hidden)').each(function() {
16081            $(this).addClass('ui-helper-hidden').prev().children('.ui-panelmenu-icon').removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e');
16082        });
16083    }
16084
16085});
16086
16087PrimeFaces.widget.TabMenu = PrimeFaces.widget.Menu.extend({
16088
16089    init: function(cfg) {
16090        this._super(cfg);
16091
16092        this.items = this.jq.find('> .ui-tabmenu-nav > li:not(.ui-state-disabled)');
16093
16094        this.bindEvents();
16095        this.bindKeyEvents();
16096    },
16097
16098    bindEvents: function() {
16099        this.items.on('mouseover.tabmenu', function(e) {
16100                    var element = $(this);
16101                    if(!element.hasClass('ui-state-active')) {
16102                        element.addClass('ui-state-hover');
16103                    }
16104                })
16105                .on('mouseout.tabmenu', function(e) {
16106                    $(this).removeClass('ui-state-hover');
16107                });
16108    },
16109
16110    bindKeyEvents: function() {
16111        /* For Keyboard accessibility and Screen Readers */
16112        this.items.attr('tabindex', 0);
16113
16114        this.items.on('focus.tabmenu', function(e) {
16115            $(this).addClass('ui-menuitem-outline');
16116        })
16117        .on('blur.tabmenu', function(){
16118            $(this).removeClass('ui-menuitem-outline');
16119        })
16120        .on('keydown.tabmenu', function(e) {
16121            var keyCode = $.ui.keyCode,
16122            key = e.which;
16123
16124            if(key === keyCode.SPACE || key === keyCode.ENTER) {
16125                var currentLink = $(this).children('a');
16126                currentLink.trigger('click');
16127                PrimeFaces.utils.openLink(e, currentLink);
16128            }
16129        });
16130    }
16131});
16132
16133/**
16134 * PrimeFaces Message Widget
16135 */
16136PrimeFaces.widget.Message = PrimeFaces.widget.BaseWidget.extend({
16137    
16138    init: function(cfg) {
16139        this._super(cfg);
16140        
16141        var text = this.jq.children('.ui-message-error-detail').text();
16142        
16143        if(text) {
16144           var target = $(PrimeFaces.escapeClientId(this.cfg.target));
16145           
16146           if (this.cfg.tooltip) {
16147              target.data('tooltip', text);
16148           }
16149           
16150           target.attr('aria-describedby', this.id + '_error-detail');
16151        } 
16152   }
16153});
16154/**
16155 * PrimeFaces NotificationBar Widget
16156 */
16157PrimeFaces.widget.NotificationBar = PrimeFaces.widget.BaseWidget.extend({
16158    
16159    init: function(cfg) {
16160        this._super(cfg);
16161        
16162        var _self = this;
16163	
16164        //relocate
16165        this.jq.css(this.cfg.position, '0').appendTo($('body'));
16166
16167        //display initially
16168        if(this.cfg.autoDisplay) {
16169            $(this.jq).css('display','block')
16170        }
16171
16172        //bind events
16173        this.jq.children('.ui-notificationbar-close').click(function() {
16174            _self.hide();
16175        });
16176    },
16177    
16178    /**
16179     * The up-to-three arguments will be routed to jQuery as it is.
16180     * @see: http://api.jquery.com/slidedown/
16181     * @see: http://api.jquery.com/fadein/
16182     * @see: http://api.jquery.com/show/
16183     */
16184    show: function(a1, a2, a3) {
16185        if(this.cfg.effect === 'slide')
16186            $(this.jq).slideDown(a1, a2, a3);
16187        else if(this.cfg.effect === 'fade')
16188            $(this.jq).fadeIn(a1, a2, a3);
16189        else if(this.cfg.effect === 'none')
16190            $(this.jq).show(a1, a2, a3);
16191    },
16192    
16193    hide: function() {
16194        if(this.cfg.effect === 'slide')
16195            $(this.jq).slideUp(this.cfg.effect);
16196        else if(this.cfg.effect === 'fade')
16197            $(this.jq).fadeOut(this.cfg.effect);
16198        else if(this.cfg.effect === 'none')
16199            $(this.jq).hide();
16200    },
16201    
16202    isVisible: function() {
16203        return this.jq.is(':visible');
16204    },
16205
16206    toggle: function() {
16207        if(this.isVisible())
16208            this.hide();
16209        else
16210            this.show();
16211    }
16212    
16213});
16214
16215/**
16216 * PrimeFaces Panel Widget
16217 */
16218PrimeFaces.widget.Panel = PrimeFaces.widget.BaseWidget.extend({
16219
16220    init: function(cfg) {
16221        this._super(cfg);
16222        this.header = this.jq.children('div.ui-panel-titlebar');
16223        this.title = this.header.children('span.ui-panel-title');
16224        this.content = $(this.jqId + '_content');
16225
16226        this.bindEvents();
16227    },
16228
16229    bindEvents: function() {
16230        var $this = this;
16231
16232        if(this.cfg.toggleable) {
16233            this.bindToggler();
16234
16235            if(this.cfg.toggleableHeader) {
16236                this.header.on('click', function() {
16237                    if(!$this.isTitlebarClicked) {
16238                        $this.toggle();
16239                    }
16240
16241                    $this.isTitlebarClicked = false;
16242                });
16243            }
16244        }
16245
16246        if(this.cfg.closable) {
16247            this.bindCloser();
16248        }
16249
16250        if(this.cfg.hasMenu) {
16251            $(this.jqId + '_menu').on('click.panel', function(e) {
16252                e.preventDefault();
16253            });
16254        }
16255
16256        //visuals for action items
16257        this.header.find('.ui-panel-titlebar-icon').on('mouseover.panel',function() {
16258            $(this).addClass('ui-state-hover');
16259        }).on('mouseout.panel',function() {
16260            $(this).removeClass('ui-state-hover');
16261        }).on('click.panel', function(e) {
16262            var href = $(this).attr('href');
16263            if(!href || href == '#') {
16264                e.preventDefault();
16265            }
16266
16267            $this.isTitlebarClicked = true;
16268        });
16269    },
16270
16271    toggle: function() {
16272        if(this.cfg.collapsed) {
16273            this.expand();
16274            PrimeFaces.invokeDeferredRenders(this.id);
16275        }
16276        else {
16277            this.collapse();
16278        }
16279    },
16280
16281    expand: function() {
16282        this.toggleState(false, 'ui-icon-plusthick', 'ui-icon-minusthick');
16283
16284        if(this.cfg.toggleOrientation === 'vertical')
16285            this.slideDown();
16286        else if(this.cfg.toggleOrientation === 'horizontal')
16287            this.slideRight();
16288    },
16289
16290    collapse: function() {
16291        this.toggleState(true, 'ui-icon-minusthick', 'ui-icon-plusthick');
16292
16293        if(this.cfg.toggleOrientation === 'vertical')
16294            this.slideUp();
16295        else if(this.cfg.toggleOrientation === 'horizontal')
16296            this.slideLeft();
16297    },
16298
16299    slideUp: function() {
16300        this.content.slideUp(this.cfg.toggleSpeed, 'easeInOutCirc');
16301    },
16302
16303    slideDown: function() {
16304        this.content.slideDown(this.cfg.toggleSpeed, 'easeInOutCirc');
16305    },
16306
16307    slideLeft: function() {
16308        var $this = this;
16309
16310        this.originalWidth = this.jq.width();
16311
16312        this.title.hide();
16313        this.toggler.hide();
16314        this.content.hide();
16315
16316        this.jq.animate({
16317            width: '42px'
16318        }, this.cfg.toggleSpeed, 'easeInOutCirc', function() {
16319            $this.toggler.show();
16320            $this.jq.addClass('ui-panel-collapsed-h');
16321        });
16322    },
16323
16324    slideRight: function() {
16325        var $this = this,
16326        expandWidth = this.originalWidth||'100%';
16327
16328        this.toggler.hide();
16329
16330        this.jq.animate({
16331            width: expandWidth
16332        }, this.cfg.toggleSpeed, 'easeInOutCirc', function() {
16333            $this.jq.removeClass('ui-panel-collapsed-h');
16334            $this.title.show();
16335            $this.toggler.show();
16336
16337            $this.content.css({
16338                'visibility': 'visible'
16339                ,'display': 'block'
16340                ,'height': 'auto'
16341            });
16342        });
16343    },
16344
16345    toggleState: function(collapsed, removeIcon, addIcon) {
16346        this.toggler.children('span.ui-icon').removeClass(removeIcon).addClass(addIcon);
16347        this.cfg.collapsed = collapsed;
16348        this.toggleStateHolder.val(collapsed);
16349
16350        this.callBehavior('toggle');
16351    },
16352
16353    close: function() {
16354        if(this.visibleStateHolder) {
16355            this.visibleStateHolder.val(false);
16356        }
16357
16358        var $this = this;
16359        this.jq.fadeOut(this.cfg.closeSpeed, function(e) {
16360            if($this.hasBehavior('close')) {
16361                $this.callBehavior('close');
16362            }
16363        });
16364    },
16365
16366    show: function() {
16367        var $this = this;
16368        $(this.jqId).fadeIn(this.cfg.closeSpeed, function() {
16369            PrimeFaces.invokeDeferredRenders($this.id);
16370        });
16371
16372        this.visibleStateHolder.val(true);
16373    },
16374
16375    bindToggler: function() {
16376        var $this = this;
16377
16378        this.toggler = $(this.jqId + '_toggler');
16379        this.toggleStateHolder = $(this.jqId + '_collapsed');
16380
16381        this.toggler.click(function() {
16382            $this.toggle();
16383
16384            return false;
16385        });
16386    },
16387
16388    bindCloser: function() {
16389        var $this = this;
16390
16391        this.closer = $(this.jqId + '_closer');
16392        this.visibleStateHolder = $(this.jqId + "_visible");
16393
16394        this.closer.click(function(e) {
16395            $this.close();
16396            e.preventDefault();
16397
16398            return false;
16399        });
16400    }
16401
16402});
16403/**
16404 * PrimeFaces OrderList Widget
16405 */
16406PrimeFaces.widget.OrderList = PrimeFaces.widget.BaseWidget.extend({
16407
16408    init: function(cfg) {
16409        this._super(cfg);
16410
16411        this.list = this.jq.find('.ui-orderlist-list'),
16412        this.items = this.list.children('.ui-orderlist-item');
16413        this.input = $(this.jqId + '_values');
16414        this.cfg.effect = this.cfg.effect||'fade';
16415        this.cfg.disabled = this.jq.hasClass('ui-state-disabled');
16416        var $this = this;
16417
16418        if(!this.cfg.disabled) {
16419            this.generateItems();
16420
16421            this.setupButtons();
16422
16423            //Enable dnd
16424            this.list.sortable({
16425                revert: 1,
16426                start: function(event, ui) {
16427                    PrimeFaces.clearSelection();
16428                }
16429                ,update: function(event, ui) {
16430                    $this.onDragDrop(event, ui);
16431                }
16432            });
16433            
16434            this.bindEvents();
16435        }
16436    },
16437
16438    generateItems: function() {
16439        var $this = this;
16440
16441        this.list.children('.ui-orderlist-item').each(function() {
16442            var item = $(this),
16443            itemValue = item.data('item-value'),
16444            option = $('<option selected="selected"></option>');
16445
16446            option.prop('value', itemValue).text(itemValue);
16447            $this.input.append(option);
16448        });
16449    },
16450
16451    bindEvents: function() {
16452        var $this = this;
16453        
16454        if (PrimeFaces.env.browser.mobile) {
16455            var disabledSortable = function() {
16456                $this.list.sortable('disable');
16457                $this.items.css('touch-action', 'auto');
16458            };
16459            
16460            disabledSortable();
16461            
16462            this.items.on('touchend.orderList-mobile', function() {
16463                disabledSortable();
16464            })
16465            .on('click.orderList-mobile', function() {
16466                $this.list.sortable('enable');
16467            });
16468        }
16469
16470        this.items.on('mouseover.orderList', function(e) {
16471            var element = $(this);
16472
16473            if(!element.hasClass('ui-state-highlight'))
16474                $(this).addClass('ui-state-hover');
16475        })
16476        .on('mouseout.orderList', function(e) {
16477            var element = $(this);
16478
16479            if(!element.hasClass('ui-state-highlight'))
16480                $(this).removeClass('ui-state-hover');
16481        })
16482        .on('mousedown.orderList', function(e) {
16483            var element = $(this),
16484            metaKey = (e.metaKey||e.ctrlKey);
16485
16486            if(!metaKey) {
16487                element.removeClass('ui-state-hover').addClass('ui-state-highlight')
16488                .siblings('.ui-state-highlight').removeClass('ui-state-highlight');
16489
16490                $this.fireItemSelectEvent(element, e);
16491            }
16492            else {
16493                if(element.hasClass('ui-state-highlight')) {
16494                    element.removeClass('ui-state-highlight');
16495                    $this.fireItemUnselectEvent(element);
16496                }
16497                else {
16498                    element.removeClass('ui-state-hover').addClass('ui-state-highlight');
16499                    $this.fireItemSelectEvent(element, e);
16500                }
16501            }
16502        });
16503    },
16504
16505    setupButtons: function() {
16506        var $this = this;
16507
16508        PrimeFaces.skinButton(this.jq.find('.ui-button'));
16509
16510        this.jq.find(' .ui-orderlist-controls .ui-orderlist-button-move-up').click(function() {$this.moveUp($this.sourceList);});
16511        this.jq.find(' .ui-orderlist-controls .ui-orderlist-button-move-top').click(function() {$this.moveTop($this.sourceList);});
16512        this.jq.find(' .ui-orderlist-controls .ui-orderlist-button-move-down').click(function() {$this.moveDown($this.sourceList);});
16513        this.jq.find(' .ui-orderlist-controls .ui-orderlist-button-move-bottom').click(function() {$this.moveBottom($this.sourceList);});
16514    },
16515
16516    onDragDrop: function(event, ui) {
16517        ui.item.removeClass('ui-state-highlight');
16518        this.saveState();
16519        this.fireReorderEvent();
16520    },
16521
16522    saveState: function() {
16523        this.input.children().remove();
16524
16525        this.generateItems();
16526    },
16527
16528    moveUp: function() {
16529        var $this = this,
16530        selectedItems = $this.list.children('.ui-orderlist-item.ui-state-highlight'),
16531        itemsToMoveCount = selectedItems.length,
16532        movedItemsCount = 0,
16533        hasFirstChild = selectedItems.is(':first-child');
16534
16535        if(hasFirstChild) {
16536            return;
16537        }
16538
16539        selectedItems.each(function() {
16540            var item = $(this);
16541
16542            if(!item.is(':first-child')) {
16543                item.hide($this.cfg.effect, {}, 'fast', function() {
16544                    item.insertBefore(item.prev()).show($this.cfg.effect, {}, 'fast', function() {
16545                        movedItemsCount++;
16546
16547                        if(itemsToMoveCount === movedItemsCount) {
16548                            $this.saveState();
16549                            $this.fireReorderEvent();
16550                        }
16551                    });
16552                });
16553            }
16554            else {
16555                itemsToMoveCount--;
16556            }
16557        });
16558    },
16559
16560    moveTop: function() {
16561        var $this = this,
16562        selectedItems = $this.list.children('.ui-orderlist-item.ui-state-highlight'),
16563        itemsToMoveCount = selectedItems.length,
16564        movedItemsCount = 0,
16565        hasFirstChild = selectedItems.is(':first-child'),
16566        firstSelectedItemIndex = selectedItems.eq(0).index();
16567
16568        if(hasFirstChild) {
16569            return;
16570        }
16571
16572        selectedItems.each(function(index) {
16573            var item = $(this),
16574                currentIndex = (index === 0) ? 0 : (item.index() - firstSelectedItemIndex);
16575
16576            if(!item.is(':first-child')) {
16577                item.hide($this.cfg.effect, {}, 'fast', function() {
16578                    item.insertBefore($this.list.children('.ui-orderlist-item').eq(currentIndex)).show($this.cfg.effect, {}, 'fast', function(){
16579                        movedItemsCount++;
16580
16581                        if(itemsToMoveCount === movedItemsCount) {
16582                            $this.saveState();
16583                            $this.fireReorderEvent();
16584                        }
16585                    });
16586                });
16587            }
16588            else {
16589                itemsToMoveCount--;
16590            }
16591        });
16592    },
16593
16594    moveDown: function() {
16595        var $this = this,
16596        selectedItems = $($this.list.children('.ui-orderlist-item.ui-state-highlight').get().reverse()),
16597        itemsToMoveCount = selectedItems.length,
16598        movedItemsCount = 0,
16599        hasFirstChild = selectedItems.is(':last-child');
16600
16601        if(hasFirstChild) {
16602            return;
16603        }
16604
16605        selectedItems.each(function() {
16606            var item = $(this);
16607
16608            if(!item.is(':last-child')) {
16609                item.hide($this.cfg.effect, {}, 'fast', function() {
16610                    item.insertAfter(item.next()).show($this.cfg.effect, {}, 'fast', function() {
16611                        movedItemsCount++;
16612
16613                        if(itemsToMoveCount === movedItemsCount) {
16614                            $this.saveState();
16615                            $this.fireReorderEvent();
16616                        }
16617                    });
16618                });
16619            }
16620            else {
16621                itemsToMoveCount--;
16622            }
16623        });
16624    },
16625
16626    moveBottom: function() {
16627        var $this = this,
16628        selectedItems = $($this.list.children('.ui-orderlist-item.ui-state-highlight').get().reverse()),
16629        itemsToMoveCount = selectedItems.length,
16630        movedItemsCount = 0,
16631        hasFirstChild = selectedItems.is(':last-child'),
16632        lastSelectedItemIndex = selectedItems.eq(0).index(),
16633        itemsLength = this.items.length;
16634
16635        if(hasFirstChild) {
16636            return;
16637        }
16638
16639        selectedItems.each(function(index) {
16640            var item = $(this),
16641                currentIndex = (index === 0) ? itemsLength - 1 : (item.index() - lastSelectedItemIndex) - 1;
16642
16643            if(!item.is(':last-child')) {
16644                item.hide($this.cfg.effect, {}, 'fast', function() {
16645                    item.insertAfter($this.list.children('.ui-orderlist-item').eq(currentIndex)).show($this.cfg.effect, {}, 'fast', function() {
16646                        movedItemsCount++;
16647
16648                        if(itemsToMoveCount === movedItemsCount) {
16649                            $this.saveState();
16650                            $this.fireReorderEvent();
16651                        }
16652                    });
16653                });
16654            }
16655            else {
16656                itemsToMoveCount--;
16657            }
16658        });
16659    },
16660
16661    fireItemSelectEvent: function(item, e) {
16662        if(this.hasBehavior('select')) {
16663            var ext = {
16664                params: [
16665                    {name: this.id + '_itemIndex', value: item.index()},
16666                    {name: this.id + '_metaKey', value: e.metaKey},
16667                    {name: this.id + '_ctrlKey', value: e.ctrlKey}
16668                ]
16669            };
16670
16671            this.callBehavior('select', ext);
16672        }
16673    },
16674
16675    fireItemUnselectEvent: function(item) {
16676        if(this.hasBehavior('unselect')) {
16677            var ext = {
16678                params: [
16679                    {name: this.id + '_itemIndex', value: item.index()}
16680                ]
16681            };
16682
16683            this.callBehavior('unselect', ext);
16684        }
16685    },
16686
16687    fireReorderEvent: function() {
16688        if(this.hasBehavior('reorder')) {
16689            this.callBehavior('reorder');
16690        }
16691    }
16692
16693});
16694/**
16695 * PrimeFaces OutputPanel Widget
16696 */
16697PrimeFaces.widget.OutputPanel = PrimeFaces.widget.BaseWidget.extend({
16698
16699    init: function(cfg) {
16700        this._super(cfg);
16701        this.cfg.global = this.cfg.global||false;
16702
16703        if(this.cfg.deferred) {
16704            if(this.cfg.deferredMode === 'load') {
16705                this.loadContent();
16706            }
16707            else if(this.cfg.deferredMode === 'visible') {
16708                if(this.visible())
16709                    this.loadContent();
16710                else
16711                    this.bindScrollMonitor();
16712            }
16713        }
16714    },
16715
16716    loadContent: function() {
16717        var $this = this,
16718        options = {
16719            source: this.id,
16720            process: this.id,
16721            update: this.id,
16722            async: true,
16723            ignoreAutoUpdate: true,
16724            global: false,
16725            params: [
16726                {name: this.id + '_load', value: true}
16727            ],
16728            onsuccess: function(responseXML, status, xhr) {
16729                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
16730                        widget: $this,
16731                        handle: function(content) {
16732                            $this.jq.html(content);
16733                        }
16734                    });
16735
16736                return true;
16737            },
16738            onerror: function(xhr, status, errorThrown) {
16739                $this.jq.html('');
16740            }
16741        };
16742
16743        if(this.hasBehavior('load')) {
16744            this.callBehavior('load', options);
16745        }
16746        else {
16747            PrimeFaces.ajax.Request.handle(options);
16748        }
16749    },
16750
16751    bindScrollMonitor: function() {
16752        var $this = this;
16753
16754        PrimeFaces.utils.registerScrollHandler(this, 'scroll.' + this.id + '_align', function() {
16755            if ($this.visible()) {
16756                PrimeFaces.utils.unbindScrollHandler($this, 'scroll.' + $this.id + '_align');
16757                $this.loadContent();
16758            }
16759        });
16760    },
16761
16762    visible: function() {
16763        var win = $(window),
16764        scrollTop = win.scrollTop(),
16765        height = win.height(),
16766        top = this.jq.offset().top,
16767        bottom = top + this.jq.innerHeight();
16768
16769        if((top >= scrollTop && top <= (scrollTop + height)) || (bottom >= scrollTop && bottom <= (scrollTop + height))) {
16770            return true;
16771        }
16772    }
16773});
16774/**
16775 * PrimeFaces OverlayPanel Widget
16776 */
16777PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.DynamicOverlayWidget.extend({
16778
16779    init: function(cfg) {
16780        this._super(cfg);
16781
16782        this.content = this.jq.children('div.ui-overlaypanel-content');
16783
16784        //configuration
16785        this.cfg.my = this.cfg.my||'left top';
16786        this.cfg.at = this.cfg.at||'left bottom';
16787        this.cfg.collision = this.cfg.collision||'flip';
16788        this.cfg.showEvent = this.cfg.showEvent||'click.ui-overlaypanel';
16789        this.cfg.hideEvent = this.cfg.hideEvent||'click.ui-overlaypanel';
16790        this.cfg.dismissable = (this.cfg.dismissable === false) ? false : true;
16791        this.cfg.showDelay = this.cfg.showDelay || 0;
16792
16793        if(this.cfg.showCloseIcon) {
16794            this.closerIcon = $('<a href="#" class="ui-overlaypanel-close ui-state-default"><span class="ui-icon ui-icon-closethick"></span></a>')
16795                              .attr('aria-label', PrimeFaces.getAriaLabel('overlaypanel.CLOSE')).appendTo(this.jq);
16796        }
16797
16798        this.bindCommonEvents();
16799
16800        if(this.cfg.target) {
16801            this.target = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.cfg.target);
16802            this.bindTargetEvents();
16803
16804            // set aria attributes
16805            this.target.attr({
16806                'aria-expanded': false,
16807                'aria-controls': this.id
16808            });
16809
16810            //dialog support
16811            this.setupDialogSupport();
16812        }
16813    },
16814
16815    //@Override
16816    refresh: function(cfg) {
16817        this._super(cfg);
16818
16819        // fix #4307
16820        this.loaded = false;
16821
16822        // see #setupDialogSupport
16823        if (!this.cfg.appendTo) {
16824            PrimeFaces.utils.removeDynamicOverlay(this, this.jq, this.id, $(document.body));
16825        }
16826    },
16827
16828    //@Override
16829    destroy: function() {
16830        this._super();
16831
16832        // see #setupDialogSupport
16833        if (!this.cfg.appendTo) {
16834            PrimeFaces.utils.removeDynamicOverlay(this, this.jq, this.id, $(document.body));
16835        }
16836    },
16837
16838    bindTargetEvents: function() {
16839        var $this = this;
16840
16841        //mark target and descandants of target as a trigger for a primefaces overlay
16842        this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
16843
16844        //show and hide events for target
16845        if(this.cfg.showEvent === this.cfg.hideEvent) {
16846            var event = this.cfg.showEvent;
16847
16848            this.target.on(event, function(e) {
16849                $this.toggle();
16850            });
16851        }
16852        else {
16853            var showEvent = this.cfg.showEvent + '.ui-overlaypanel',
16854            hideEvent = this.cfg.hideEvent + '.ui-overlaypanel';
16855
16856            this.target.off(showEvent + ' ' + hideEvent).on(showEvent, function(e) {
16857                if(!$this.isVisible()) {
16858                    $this.show();
16859                    if(showEvent === 'contextmenu.ui-overlaypanel') {
16860                        e.preventDefault();
16861                    }
16862                }
16863            })
16864            .on(hideEvent, function(e) {
16865            	clearTimeout($this.showTimeout);
16866                if($this.isVisible()) {
16867                    $this.hide();
16868                }
16869            });
16870        }
16871
16872        $this.target.off('keydown.ui-overlaypanel keyup.ui-overlaypanel')
16873        .on('keydown.ui-overlaypanel', PrimeFaces.utils.blockEnterKey)
16874        .on('keyup.ui-overlaypanel', function(e) {
16875            var keyCode = $.ui.keyCode, key = e.which;
16876
16877            if(key === keyCode.ENTER) {
16878                $this.toggle();
16879                e.preventDefault();
16880            }
16881        });
16882
16883    },
16884
16885    bindCommonEvents: function() {
16886        var $this = this;
16887
16888        if(this.cfg.showCloseIcon) {
16889            this.closerIcon.on('mouseover.ui-overlaypanel', function() {
16890                $(this).addClass('ui-state-hover');
16891            })
16892            .on('mouseout.ui-overlaypanel', function() {
16893                $(this).removeClass('ui-state-hover');
16894            })
16895            .on('click.ui-overlaypanel', function(e) {
16896                $this.hide();
16897                e.preventDefault();
16898            })
16899            .on('focus.ui-overlaypanel', function() {
16900                $(this).addClass('ui-state-focus');
16901            })
16902            .on('blur.ui-overlaypanel', function() {
16903                $(this).removeClass('ui-state-focus');
16904            });
16905        }
16906
16907        //hide overlay when mousedown is at outside of overlay
16908        if(this.cfg.dismissable && !this.cfg.modal) {
16909            PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', $this.jq,
16910                function() { return $this.target; },
16911                function(e, eventTarget) {
16912                    if(!($this.jq.is(eventTarget) || $this.jq.has(eventTarget).length > 0 || eventTarget.closest('.ui-input-overlay').length > 0)) {
16913                        $this.hide();
16914                    }
16915                });
16916        }
16917
16918        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.jq, function() {
16919            $this.align();
16920        });
16921    },
16922
16923    toggle: function() {
16924        if(!this.isVisible()) {
16925            this.show();
16926        }
16927        else {
16928            clearTimeout(this.showTimeout);
16929            this.hide();
16930        }
16931    },
16932
16933    show: function(target) {
16934    	var thisPanel = this;
16935        this.showTimeout = setTimeout(function() {
16936            if (!thisPanel.loaded && thisPanel.cfg.dynamic) {
16937                thisPanel.loadContents(target);
16938            }
16939            else {
16940                thisPanel._show(target);
16941            }
16942        }, this.cfg.showDelay);
16943    },
16944
16945    _show: function(target) {
16946        var $this = this;
16947
16948        this.align(target);
16949
16950        //replace visibility hidden with display none for effect support, toggle marker class
16951        this.jq.removeClass('ui-overlay-hidden').addClass('ui-overlay-visible').css({
16952            'display':'none'
16953        });
16954
16955        if(this.cfg.showEffect) {
16956            this.jq.show(this.cfg.showEffect, {}, 200, function() {
16957                $this.postShow();
16958            });
16959        }
16960        else {
16961            this.jq.show();
16962            this.postShow();
16963        }
16964
16965        if(this.cfg.modal) {
16966            this.enableModality();
16967        }
16968    },
16969
16970    align: function(target) {
16971        var win = $(window);
16972
16973        if (target) {
16974            if (typeof target === 'string') {
16975                this.targetElement = $(document.getElementById(target));
16976            }
16977            else if (target instanceof $) {
16978                this.targetElement = target;
16979            }
16980        }
16981        else if (this.target) {
16982            this.targetElement = this.target;
16983        }
16984
16985        if (this.targetElement) {
16986            this.targetZindex = this.targetElement.zIndex();
16987        }
16988
16989        this.jq.css({'left':'', 'top':'', 'z-index': ++PrimeFaces.zindex})
16990                .position({
16991                    my: this.cfg.my
16992                    ,at: this.cfg.at
16993                    ,of: this.targetElement
16994                    ,collision: this.cfg.collision
16995                });
16996
16997        var widthOffset = this.jq.width() - this.content.width();
16998        this.jq.css('max-width', win.width() - widthOffset + 'px');
16999    },
17000
17001    hide: function() {
17002        var $this = this;
17003
17004        if(this.cfg.hideEffect) {
17005            this.jq.hide(this.cfg.hideEffect, {}, 200, function() {
17006                if($this.cfg.modal) {
17007                    $this.disableModality();
17008                }
17009                $this.postHide();
17010            });
17011        }
17012        else {
17013            this.jq.hide();
17014            if($this.cfg.modal) {
17015                $this.disableModality();
17016            }
17017            this.postHide();
17018        }
17019    },
17020
17021    postShow: function() {
17022
17023        this.callBehavior('show');
17024
17025        if(this.cfg.onShow) {
17026            this.cfg.onShow.call(this);
17027        }
17028
17029        this.applyFocus();
17030
17031        if (this.target) {
17032            this.target.attr('aria-expanded', true);
17033        }
17034    },
17035
17036    postHide: function() {
17037        //replace display block with visibility hidden for hidden container support, toggle marker class
17038        this.jq.removeClass('ui-overlay-visible').addClass('ui-overlay-hidden').css({
17039            'display':'block'
17040        });
17041
17042        this.callBehavior('hide');
17043
17044        if(this.cfg.onHide) {
17045            this.cfg.onHide.call(this);
17046        }
17047
17048        if (this.target) {
17049            this.target.attr('aria-expanded', false);
17050        }
17051    },
17052
17053    setupDialogSupport: function() {
17054        var dialog = this.target[0].closest('.ui-dialog');
17055        if (dialog) {
17056            var $dialog = $(dialog);
17057            if ($dialog.length == 1) {
17058                //set position as fixed to scroll with dialog
17059                if($dialog.css('position') === 'fixed') {
17060                    this.jq.css('position', 'fixed');
17061                }
17062
17063                //append to body if not already appended by user choice
17064                if(!this.cfg.appendTo) {
17065                    this.jq.appendTo(document.body);
17066                }
17067            }
17068        }
17069    },
17070
17071    loadContents: function(target) {
17072        var $this = this,
17073        options = {
17074            source: this.id,
17075            process: this.id,
17076            update: this.id,
17077            params: [
17078                {name: this.id + '_contentLoad', value: true}
17079            ],
17080            onsuccess: function(responseXML, status, xhr) {
17081                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
17082                        widget: $this,
17083                        handle: function(content) {
17084                            this.content.html(content);
17085                            this.loaded = true;
17086                        }
17087                    });
17088
17089                return true;
17090            },
17091            oncomplete: function() {
17092                $this._show(target);
17093            }
17094        };
17095
17096        PrimeFaces.ajax.Request.handle(options);
17097    },
17098
17099    isVisible: function() {
17100        return this.jq.hasClass('ui-overlay-visible');
17101    },
17102
17103    applyFocus: function() {
17104        this.jq.find(':not(:submit):not(:button):input:visible:enabled:first').focus();
17105    },
17106
17107    //@override
17108    enableModality: function() {
17109        this._super();
17110
17111        if(this.targetElement) {
17112            this.targetElement.css('z-index', this.jq.css('z-index'));
17113        }
17114    },
17115
17116    //@override
17117    disableModality: function(){
17118        this._super();
17119
17120        if(this.targetElement) {
17121            this.targetElement.css('z-index', this.targetZindex);
17122        }
17123    },
17124
17125    //@override
17126    getModalTabbables: function(){
17127        var tabbables = this.jq.find(':tabbable');
17128
17129        if (this.targetElement && this.targetElement.is(':tabbable')) {
17130            tabbables = tabbables.add(this.targetElement);
17131        }
17132
17133        return tabbables;
17134    }
17135});
17136
17137PrimeFaces.widget.Paginator = PrimeFaces.widget.BaseWidget.extend({
17138
17139    init: function(cfg) {
17140        this.cfg = cfg;
17141        this.jq = $();
17142
17143        var _self = this;
17144        $.each(this.cfg.id, function(index, id){
17145            _self.jq = _self.jq.add($(PrimeFaces.escapeClientId(id)));
17146        });
17147
17148        //elements
17149        this.pagesContainer = this.jq.children('.ui-paginator-pages');
17150        this.pageLinks = this.pagesContainer.children('.ui-paginator-page');
17151        this.rppSelect = this.jq.children('.ui-paginator-rpp-options');
17152        this.jtpSelect = this.jq.children('.ui-paginator-jtp-select');
17153        this.jtpInput = this.jq.children('.ui-paginator-jtp-input');
17154        this.firstLink = this.jq.children('.ui-paginator-first');
17155        this.prevLink  = this.jq.children('.ui-paginator-prev');
17156        this.nextLink  = this.jq.children('.ui-paginator-next');
17157        this.endLink   = this.jq.children('.ui-paginator-last');
17158        this.currentReport = this.jq.children('.ui-paginator-current');
17159
17160        //metadata
17161        this.cfg.rows = this.cfg.rows == 0 ? this.cfg.rowCount : this.cfg.rows;
17162        this.cfg.prevRows = this.cfg.rows;
17163        this.cfg.pageCount = Math.ceil(this.cfg.rowCount / this.cfg.rows)||1;
17164        this.cfg.pageLinks = this.cfg.pageLinks||10;
17165        this.cfg.currentPageTemplate = this.cfg.currentPageTemplate||'({currentPage} of {totalPages})';
17166
17167        //aria message
17168        this.cfg.ariaPageLabel = PrimeFaces.getAriaLabel('paginator.PAGE');
17169
17170        //event bindings
17171        this.bindEvents();
17172    },
17173
17174    bindEvents: function(){
17175        var $this = this;
17176
17177        //visuals for first,prev,next,last buttons
17178        this.jq.children('a.ui-state-default').on('mouseover.paginator', function(){
17179            var item = $(this);
17180            if(!item.hasClass('ui-state-disabled')) {
17181                item.addClass('ui-state-hover');
17182            }
17183        })
17184        .on('mouseout.paginator', function() {
17185            $(this).removeClass('ui-state-hover');
17186        })
17187        .on('focus.paginator', function() {
17188            var item = $(this);
17189            if(!item.hasClass('ui-state-disabled')) {
17190                item.addClass('ui-state-focus');
17191            }
17192        })
17193        .on('blur.paginator', function() {
17194            $(this).removeClass('ui-state-focus');
17195        })
17196        .on('keydown.paginator', function(e) {
17197            var key = e.which,
17198            keyCode = $.ui.keyCode;
17199
17200            if((key === keyCode.ENTER)) {
17201                $(this).trigger('click');
17202                e.preventDefault();
17203            }
17204        });
17205
17206        //page links
17207        this.bindPageLinkEvents();
17208
17209        //records per page selection
17210        PrimeFaces.skinSelect(this.rppSelect);
17211        this.rppSelect.change(function(e) {
17212            if(!$(this).hasClass("ui-state-disabled")){
17213                $this.setRowsPerPage($(this).val());
17214            }
17215        });
17216
17217        //jump to page dropdown
17218        PrimeFaces.skinSelect(this.jtpSelect);
17219        this.jtpSelect.change(function(e) {
17220            if(!$(this).hasClass("ui-state-disabled")){
17221                $this.setPage(parseInt($(this).val()));
17222            }
17223        });
17224
17225        //jump to page input
17226        PrimeFaces.skinInput(this.jtpInput);
17227        this.jtpInput.change(function(e) {
17228            if(!$(this).hasClass("ui-state-disabled")){
17229                var page = parseInt($(this).val());
17230                if (isNaN(page) || page > $this.cfg.pageCount || page < 1) {
17231                    // restore old value if invalid
17232                    $(this).val($this.cfg.page + 1);
17233                }
17234                else {
17235                    $this.setPage(page - 1);
17236                }
17237            }
17238        });
17239
17240        //First page link
17241        this.firstLink.click(function(e) {
17242            PrimeFaces.clearSelection();
17243
17244            if(!$(this).hasClass("ui-state-disabled")){
17245                $this.setPage(0);
17246            }
17247
17248            e.preventDefault();
17249        });
17250
17251        //Prev page link
17252        this.prevLink.click(function(e) {
17253            PrimeFaces.clearSelection();
17254
17255            if(!$(this).hasClass("ui-state-disabled")){
17256                $this.setPage($this.cfg.page - 1);
17257            }
17258
17259            e.preventDefault();
17260        });
17261
17262        //Next page link
17263        this.nextLink.click(function(e) {
17264            PrimeFaces.clearSelection();
17265
17266            if(!$(this).hasClass("ui-state-disabled")){
17267                $this.setPage($this.cfg.page + 1);
17268            }
17269
17270            e.preventDefault();
17271        });
17272
17273        //Last page link
17274        this.endLink.click(function(e) {
17275            PrimeFaces.clearSelection();
17276
17277            if(!$(this).hasClass("ui-state-disabled")){
17278                $this.setPage($this.cfg.pageCount - 1);
17279            }
17280
17281            e.preventDefault();
17282        });
17283    },
17284
17285    bindPageLinkEvents: function(){
17286        var $this = this,
17287        pageLinks = this.pagesContainer.children('.ui-paginator-page');
17288
17289        pageLinks.each(function() {
17290            var link = $(this),
17291            pageNumber = parseInt(link.text());
17292
17293            link.attr('aria-label', $this.cfg.ariaPageLabel.replace('{0}', (pageNumber)));
17294        });
17295
17296        pageLinks.on('click.paginator', function(e) {
17297            var link = $(this),
17298            pageNumber = parseInt(link.text());
17299
17300            if(!link.hasClass('ui-state-disabled')&&!link.hasClass('ui-state-active')) {
17301                $this.setPage(pageNumber - 1);
17302            }
17303
17304            e.preventDefault();
17305        })
17306        .on('mouseover.paginator', function() {
17307            var item = $(this);
17308            if(!item.hasClass('ui-state-disabled')&&!item.hasClass('ui-state-active')) {
17309                item.addClass('ui-state-hover');
17310            }
17311        })
17312        .on('mouseout.paginator', function() {
17313            $(this).removeClass('ui-state-hover');
17314        })
17315        .on('focus.paginator', function() {
17316            $(this).addClass('ui-state-focus');
17317        })
17318        .on('blur.paginator', function() {
17319            $(this).removeClass('ui-state-focus');
17320        })
17321        .on('keydown.paginator', function(e) {
17322            var key = e.which,
17323            keyCode = $.ui.keyCode;
17324
17325            if((key === keyCode.ENTER)) {
17326                $(this).trigger('click');
17327                e.preventDefault();
17328            }
17329        });
17330    },
17331
17332    /**
17333     * Binds swipe events to this paginator to the JQ element passed in.
17334     * 
17335     * @param owner the owner of the paginator
17336     */
17337    bindSwipeEvents: function(owner) {
17338        if (!PrimeFaces.env.touch) {
17339            return;
17340        }
17341        var $this = this;
17342        owner.swipe({
17343            swipeLeft:function(event) {
17344                $this.prev();
17345            },
17346            swipeRight: function(event) {
17347                $this.next();
17348            },
17349            excludedElements: PrimeFaces.utils.excludedSwipeElements()
17350        });
17351    },
17352
17353    unbindEvents: function() {
17354        var buttons = this.jq.children('a.ui-state-default');
17355        if (buttons.length > 0) {
17356            buttons.off();
17357        }
17358        var pageLinks = this.pagesContainer.children('.ui-paginator-page');
17359        if (pageLinks.length > 0) {
17360            pageLinks.off();
17361        }
17362    },
17363
17364    updateUI: function() {
17365        //boundaries
17366        if(this.cfg.page === 0) {
17367            this.disableElement(this.firstLink);
17368            this.disableElement(this.prevLink);
17369        }
17370        else {
17371            this.enableElement(this.firstLink);
17372            this.enableElement(this.prevLink);
17373        }
17374
17375        if(this.cfg.page === (this.cfg.pageCount - 1)) {
17376            this.disableElement(this.nextLink);
17377            this.disableElement(this.endLink);
17378        }
17379        else {
17380            this.enableElement(this.nextLink);
17381            this.enableElement(this.endLink);
17382        }
17383
17384        //current page report
17385        var startRecord = (this.cfg.rowCount === 0) ? 0 : (this.cfg.page * this.cfg.rows) + 1,
17386        endRecord = (this.cfg.page * this.cfg.rows) + this.cfg.rows;
17387        if(endRecord > this.cfg.rowCount) {
17388            endRecord = this.cfg.rowCount;
17389        }
17390
17391        var text = this.cfg.currentPageTemplate
17392            .replace("{currentPage}", this.cfg.page + 1)
17393            .replace("{totalPages}", this.cfg.pageCount)
17394            .replace("{totalRecords}", this.cfg.rowCount)
17395            .replace("{startRecord}", startRecord)
17396            .replace("{endRecord}", endRecord);
17397        this.currentReport.text(text);
17398
17399        //rows per page dropdown
17400        if(this.cfg.prevRows !== this.cfg.rows) {
17401            this.rppSelect.filter(':not(.ui-state-focus)').children('option').filter('option[value="' + $.escapeSelector(this.cfg.rows) + '"]').prop('selected', true);
17402            this.cfg.prevRows = this.cfg.rows;
17403        }
17404
17405        //jump to page dropdown
17406        if(this.jtpSelect.length > 0) {
17407            if(this.jtpSelect[0].options.length != this.cfg.pageCount){
17408                var jtpOptions = '';
17409                for(var i = 0; i < this.cfg.pageCount; i++) {
17410                    jtpOptions += '<option value="' + i + '">' + (i + 1) + '</option>';
17411                }
17412
17413                this.jtpSelect.html(jtpOptions);
17414            }
17415            this.jtpSelect.children('option[value=' + this.cfg.page + ']').prop('selected','selected');
17416        }
17417
17418        //jump to page input
17419        if(this.jtpInput.length > 0) {
17420            this.jtpInput.val(this.cfg.page + 1);
17421        }
17422
17423        //page links
17424        this.updatePageLinks();
17425    },
17426
17427    updatePageLinks: function() {
17428        var start, end, delta,
17429        focusedElement = $(document.activeElement),
17430        focusContainer;
17431
17432        if(focusedElement.hasClass('ui-paginator-page')) {
17433            var pagesContainerIndex = this.pagesContainer.index(focusedElement.parent());
17434            if(pagesContainerIndex >= 0) {
17435                focusContainer = this.pagesContainer.eq(pagesContainerIndex);
17436            }
17437        }
17438
17439        //calculate visible page links
17440        this.cfg.pageCount = Math.ceil(this.cfg.rowCount / this.cfg.rows)||1;
17441        var visiblePages = Math.min(this.cfg.pageLinks, this.cfg.pageCount);
17442
17443        //calculate range, keep current in middle if necessary
17444        start = Math.max(0, Math.ceil(this.cfg.page - ((visiblePages) / 2)));
17445        end = Math.min(this.cfg.pageCount - 1, start + visiblePages - 1);
17446
17447        //check when approaching to last page
17448        delta = this.cfg.pageLinks - (end - start + 1);
17449        start = Math.max(0, start - delta);
17450
17451        //update dom
17452        this.pagesContainer.children().remove();
17453        for(var i = start; i <= end; i++) {
17454            var styleClass = 'ui-paginator-page ui-state-default ui-corner-all',
17455            ariaLabel = this.cfg.ariaPageLabel.replace('{0}', (i+1));
17456
17457            if(this.cfg.page == i) {
17458                styleClass += " ui-state-active";
17459            }
17460
17461            this.pagesContainer.append('<a class="' + styleClass + '" aria-label="' + ariaLabel + '" tabindex="0" href="#">' + (i + 1) + '</a>');
17462        }
17463
17464        if(focusContainer) {
17465            focusContainer.children().filter('.ui-state-active').trigger('focus');
17466        }
17467
17468        this.bindPageLinkEvents();
17469    },
17470
17471    setPage: function(p, silent) {
17472        if(p >= 0 && p < this.cfg.pageCount && this.cfg.page != p){
17473            var newState = {
17474                first: this.cfg.rows * p,
17475                rows: this.cfg.rows,
17476                page: p
17477            };
17478
17479            if(silent) {
17480                this.cfg.page = p;
17481                this.updateUI();
17482            }
17483            else {
17484                this.cfg.paginate.call(this, newState);
17485            }
17486        }
17487    },
17488
17489    setRowsPerPage: function(rpp) {
17490        if (rpp === '*') {
17491            this.cfg.rows = this.cfg.rowCount;
17492            this.cfg.pageCount = 1;
17493            this.cfg.page = 0;
17494
17495            var newState = {
17496                first: 0,
17497                rows: rpp,
17498                page: this.cfg.page
17499            };
17500
17501            this.cfg.paginate.call(this, newState);
17502        }
17503        else {
17504            var first = this.cfg.rows * this.cfg.page;
17505            this.cfg.rows = parseInt(rpp);
17506            var page = parseInt(first / this.cfg.rows);
17507
17508            this.cfg.pageCount = Math.ceil(this.cfg.rowCount / this.cfg.rows);
17509            this.cfg.page = -1;
17510
17511            this.setPage(page);
17512        }
17513    },
17514
17515    setTotalRecords: function(value) {
17516        this.cfg.rowCount = value;
17517        this.cfg.pageCount = Math.ceil(value / this.cfg.rows)||1;
17518        this.cfg.page = 0;
17519        this.updateUI();
17520    },
17521
17522    updateTotalRecords: function(value) {
17523        this.cfg.rowCount = value;
17524        this.cfg.pageCount = Math.ceil(value / this.cfg.rows)||1;
17525        this.updateUI();
17526    },
17527
17528    getCurrentPage: function() {
17529        return this.cfg.page;
17530    },
17531
17532    getFirst: function() {
17533        return (this.cfg.rows * this.cfg.page);
17534    },
17535
17536    getRows: function() {
17537        return this.cfg.rows;
17538    },
17539
17540    getContainerHeight: function(margin) {
17541        var height = 0;
17542
17543        for(var i = 0; i < this.jq.length; i++) {
17544            height += this.jq.eq(i).outerHeight(margin);
17545        }
17546
17547        return height;
17548    },
17549
17550    disableElement: function(element) {
17551        element.removeClass('ui-state-hover ui-state-focus ui-state-active').addClass('ui-state-disabled').attr('tabindex', -1);
17552        element.removeClass('ui-state-hover ui-state-focus ui-state-active').addClass('ui-state-disabled').attr('tabindex', -1);
17553    },
17554
17555    enableElement: function(element) {
17556        element.removeClass('ui-state-disabled').attr('tabindex', 0);
17557    },
17558
17559    next: function() {
17560        this.setPage(this.cfg.page + 1);
17561    },
17562
17563    prev: function() {
17564        this.setPage(this.cfg.page - 1);
17565    }
17566});
17567
17568/**
17569 * PrimeFaces PickList Widget
17570 */
17571PrimeFaces.widget.PickList = PrimeFaces.widget.BaseWidget.extend({
17572
17573    init: function(cfg) {
17574        this._super(cfg);
17575
17576        this.sourceList = this.jq.find('ul.ui-picklist-source');
17577        this.targetList = this.jq.find('ul.ui-picklist-target');
17578        this.sourceInput = $(this.jqId + '_source');
17579        this.targetInput = $(this.jqId + '_target');
17580        this.items = this.jq.find('.ui-picklist-item:not(.ui-state-disabled)');
17581        if(this.cfg.showCheckbox) {
17582            this.checkboxes = this.items.find('div.ui-chkbox > div.ui-chkbox-box');
17583        }
17584        this.focusedItem = null;
17585        this.ariaRegion = $(this.jqId + '_ariaRegion');
17586
17587        var sourceCaption = this.sourceList.prev('.ui-picklist-caption'),
17588            targetCaption = this.targetList.prev('.ui-picklist-caption');
17589
17590        if(sourceCaption.length) {
17591            var captionText = sourceCaption.text();
17592
17593            this.sourceList.attr('aria-label', captionText);
17594            this.sourceInput.attr('title', captionText);
17595        }
17596
17597        if(targetCaption.length) {
17598            var captionText = targetCaption.text();
17599
17600            this.targetList.attr('aria-label', captionText);
17601            this.targetInput.attr('title', captionText);
17602        }
17603
17604        this.setTabIndex();
17605
17606        //generate input options
17607        this.generateItems(this.sourceList, this.sourceInput);
17608        this.generateItems(this.targetList, this.targetInput);
17609
17610        if(this.cfg.disabled) {
17611            $(this.jqId + ' li.ui-picklist-item').addClass('ui-state-disabled');
17612            $(this.jqId + ' button').attr('disabled', 'disabled').addClass('ui-state-disabled');
17613            $(this.jqId + ' .ui-picklist-filter-container').addClass('ui-state-disabled').children('input').attr('disabled', 'disabled');
17614        }
17615        else {
17616            var $this = this,
17617                reordered = true;
17618
17619            //Sortable lists
17620            $(this.jqId + ' ul').sortable({
17621                cancel: '.ui-state-disabled,.ui-chkbox-box',
17622                connectWith: this.jqId + ' .ui-picklist-list',
17623                revert: 1,
17624                helper: 'clone',
17625                update: function(event, ui) {
17626                    $this.unselectItem(ui.item);
17627
17628                    $this.saveState();
17629                    if(reordered) {
17630                        $this.fireReorderEvent();
17631                        reordered = false;
17632                    }
17633                },
17634                receive: function(event, ui) {
17635                    $this.fireTransferEvent(ui.item, ui.sender, ui.item.parents('ul.ui-picklist-list:first'), 'dragdrop');
17636                },
17637
17638                start: function(event, ui) {
17639                    $this.itemListName = $this.getListName(ui.item);
17640                    $this.dragging = true;
17641                },
17642
17643                stop: function(event, ui) {
17644                    $this.dragging = false;
17645                },
17646
17647                beforeStop:function(event, ui) {
17648                    if($this.itemListName !== $this.getListName(ui.item)) {
17649                        reordered = false;
17650                    }
17651                    else {
17652                        reordered = true;
17653                    }
17654                }
17655            });
17656
17657            this.bindItemEvents();
17658
17659            this.bindButtonEvents();
17660
17661            this.bindFilterEvents();
17662
17663            this.bindKeyEvents();
17664
17665            this.updateButtonsState();
17666
17667            this.updateListRole();
17668        }
17669    },
17670
17671    bindItemEvents: function() {
17672        var $this = this;
17673
17674        this.items.on('mouseover.pickList', function(e) {
17675            var element = $(this);
17676
17677            if(!element.hasClass('ui-state-highlight')) {
17678                $(this).addClass('ui-state-hover');
17679            }
17680        })
17681        .on('mouseout.pickList', function(e) {
17682            $(this).removeClass('ui-state-hover');
17683        })
17684        .on('click.pickList', function(e) {
17685            //stop propagation
17686            if($this.checkboxClick||$this.dragging) {
17687                $this.checkboxClick = false;
17688                return;
17689            }
17690
17691            var item = $(this),
17692            parentList = item.parent(),
17693            metaKey = (e.metaKey||e.ctrlKey);
17694
17695            if(!e.shiftKey) {
17696                if(!metaKey) {
17697                    $this.unselectAll();
17698                }
17699
17700                if(metaKey && item.hasClass('ui-state-highlight')) {
17701                    $this.unselectItem(item, true);
17702                }
17703                else {
17704                    $this.selectItem(item, true);
17705                    $this.cursorItem = item;
17706                }
17707            }
17708            else {
17709                $this.unselectAll();
17710
17711                if($this.cursorItem && ($this.cursorItem.parent().is(item.parent()))) {
17712                    var currentItemIndex = item.index(),
17713                    cursorItemIndex = $this.cursorItem.index(),
17714                    startIndex = (currentItemIndex > cursorItemIndex) ? cursorItemIndex : currentItemIndex,
17715                    endIndex = (currentItemIndex > cursorItemIndex) ? (currentItemIndex + 1) : (cursorItemIndex + 1);
17716
17717                    for(var i = startIndex ; i < endIndex; i++) {
17718                        var it = parentList.children('li.ui-picklist-item').eq(i);
17719
17720                        if(it.is(':visible')) {
17721                            if(i === (endIndex - 1))
17722                                $this.selectItem(it, true);
17723                            else
17724                                $this.selectItem(it);
17725                        }
17726                    }
17727                }
17728                else {
17729                    $this.selectItem(item, true);
17730                    $this.cursorItem = item;
17731                }
17732            }
17733
17734            /* For keyboard navigation */
17735            $this.removeOutline();
17736            $this.focusedItem = item;
17737            parentList.trigger('focus.pickList');
17738        })
17739        .on('dblclick.pickList', function() {
17740            var item = $(this);
17741
17742            if($(this).parent().hasClass('ui-picklist-source'))
17743                $this.transfer(item, $this.sourceList, $this.targetList, 'dblclick');
17744            else
17745                $this.transfer(item, $this.targetList, $this.sourceList, 'dblclick');
17746
17747            /* For keyboard navigation */
17748            $this.removeOutline();
17749            $this.focusedItem = null;
17750
17751            PrimeFaces.clearSelection();
17752        });
17753
17754        if(this.cfg.showCheckbox) {
17755            this.checkboxes.on('mouseover.pickList', function(e) {
17756                var chkbox = $(this);
17757
17758                if(!chkbox.hasClass('ui-state-active'))
17759                    chkbox.addClass('ui-state-hover');
17760            })
17761            .on('mouseout.pickList', function(e) {
17762                $(this).removeClass('ui-state-hover');
17763            })
17764            .on('click.pickList', function(e) {
17765                $this.checkboxClick = true;
17766
17767                var item = $(this).closest('li.ui-picklist-item');
17768                if(item.hasClass('ui-state-highlight')) {
17769                    $this.unselectItem(item, true);
17770                }
17771                else {
17772                    $this.selectItem(item, true);
17773                }
17774                $this.focusedItem = item;
17775            });
17776        }
17777    },
17778
17779    bindKeyEvents: function() {
17780        var $this = this,
17781            listSelector = 'ul.ui-picklist-source, ul.ui-picklist-target';
17782
17783        this.jq.off('focus.pickList blur.pickList keydown.pickList', listSelector).on('focus.pickList', listSelector, null, function(e) {
17784            var list = $(this),
17785                activeItem = $this.focusedItem||list.children('.ui-state-highlight:visible:first');
17786            if(activeItem.length) {
17787                $this.focusedItem = activeItem;
17788            }
17789            else {
17790                $this.focusedItem = list.children('.ui-picklist-item:visible:first');
17791            }
17792
17793            setTimeout(function() {
17794                if ($this.focusedItem) {
17795                    PrimeFaces.scrollInView(list, $this.focusedItem);
17796                    $this.focusedItem.addClass('ui-picklist-outline');
17797                    $this.ariaRegion.text($this.focusedItem.data('item-label'));
17798                }
17799            }, 100);
17800        })
17801        .on('blur.pickList', listSelector, null, function() {
17802            $this.removeOutline();
17803            $this.focusedItem = null;
17804        })
17805        .on('keydown.pickList', listSelector, null, function(e) {
17806
17807            if(!$this.focusedItem) {
17808                return;
17809            }
17810
17811            var list = $(this),
17812                keyCode = $.ui.keyCode,
17813                key = e.which;
17814
17815            switch(key) {
17816                case keyCode.UP:
17817                    $this.removeOutline();
17818
17819                    if(!$this.focusedItem.hasClass('ui-state-highlight')) {
17820                        $this.selectItem($this.focusedItem);
17821                    }
17822                    else {
17823                        var prevItem = $this.focusedItem.prevAll('.ui-picklist-item:visible:first');
17824                        if(prevItem.length) {
17825                            $this.unselectAll();
17826                            $this.selectItem(prevItem);
17827                            $this.focusedItem = prevItem;
17828
17829                            PrimeFaces.scrollInView(list, $this.focusedItem);
17830                        }
17831                    }
17832                    $this.ariaRegion.text($this.focusedItem.data('item-label'));
17833                    e.preventDefault();
17834                break;
17835
17836                case keyCode.DOWN:
17837                    $this.removeOutline();
17838
17839                    if(!$this.focusedItem.hasClass('ui-state-highlight')) {
17840                        $this.selectItem($this.focusedItem);
17841                    }
17842                    else {
17843                        var nextItem = $this.focusedItem.nextAll('.ui-picklist-item:visible:first');
17844                        if(nextItem.length) {
17845                            $this.unselectAll();
17846                            $this.selectItem(nextItem);
17847                            $this.focusedItem = nextItem;
17848
17849                            PrimeFaces.scrollInView(list, $this.focusedItem);
17850                        }
17851                    }
17852                    $this.ariaRegion.text($this.focusedItem.data('item-label'));
17853                    e.preventDefault();
17854                break;
17855
17856                case keyCode.ENTER:
17857                case keyCode.SPACE:
17858                    if($this.focusedItem && $this.focusedItem.hasClass('ui-state-highlight')) {
17859                        $this.focusedItem.trigger('dblclick.pickList');
17860                        $this.focusedItem = null;
17861                    }
17862                    e.preventDefault();
17863                break;
17864                default:
17865                    // #3304 find first item matching the character typed
17866                    var keyChar = String.fromCharCode(key).toLowerCase();
17867                    list.children('.ui-picklist-item').each(function() {
17868                        var item = $(this),
17869                            itemLabel = item.attr('data-item-label');
17870                        if (itemLabel.toLowerCase().indexOf(keyChar) === 0) {
17871                            $this.removeOutline();
17872                            $this.unselectAll();
17873                            $this.selectItem(item);
17874                            $this.focusedItem = item;
17875                            PrimeFaces.scrollInView(list, $this.focusedItem);
17876                            $this.ariaRegion.text($this.focusedItem.data('item-label'));
17877                            e.preventDefault();
17878                            return false;
17879                        }
17880                    });
17881                break;
17882            };
17883        });
17884
17885    },
17886
17887    removeOutline: function() {
17888        if(this.focusedItem && this.focusedItem.hasClass('ui-picklist-outline')) {
17889            this.focusedItem.removeClass('ui-picklist-outline');
17890        }
17891    },
17892
17893    selectItem: function(item, silent) {
17894        item.removeClass('ui-state-hover').addClass('ui-state-highlight');
17895
17896        if(this.cfg.showCheckbox) {
17897            this.selectCheckbox(item.find('div.ui-chkbox-box'));
17898        }
17899
17900        if(silent) {
17901            this.fireItemSelectEvent(item);
17902        }
17903
17904        this.updateButtonsState();
17905    },
17906
17907    unselectItem: function(item, silent) {
17908        item.removeClass('ui-state-hover ui-state-highlight');
17909
17910        if(this.cfg.showCheckbox) {
17911            this.unselectCheckbox(item.find('div.ui-chkbox-box'));
17912        }
17913
17914        if(silent) {
17915            this.fireItemUnselectEvent(item);
17916        }
17917
17918        this.updateButtonsState();
17919    },
17920
17921    unselectAll: function() {
17922        var selectedItems = this.items.filter('.ui-state-highlight');
17923        for(var i = 0; i < selectedItems.length; i++) {
17924            this.unselectItem(selectedItems.eq(i));
17925        }
17926    },
17927
17928    selectCheckbox: function(chkbox) {
17929        chkbox.removeClass('ui-state-hover').addClass('ui-state-active').children('span.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check');
17930    },
17931
17932    unselectCheckbox: function(chkbox) {
17933        chkbox.removeClass('ui-state-active').children('span.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check');
17934    },
17935
17936    generateItems: function(list, input) {
17937        var $this = this;
17938        list.children('.ui-picklist-item').each(function() {
17939            var item = $(this),
17940            itemValue = item.attr('data-item-value'),
17941            itemLabel = item.attr('data-item-label') ? PrimeFaces.escapeHTML(item.attr('data-item-label')) : '',
17942            option = $('<option selected="selected"></option>');
17943
17944            if ($this.cfg.escapeValue) {
17945               itemValue = PrimeFaces.escapeHTML(itemValue);
17946            }
17947            option.prop('value', itemValue).text(itemLabel);
17948            input.append(option);
17949        });
17950    },
17951
17952    bindButtonEvents: function() {
17953        var _self = this;
17954
17955        //visuals
17956        PrimeFaces.skinButton(this.jq.find('.ui-button'));
17957
17958        //events
17959        $(this.jqId + ' .ui-picklist-button-add').click(function() {_self.add();});
17960        $(this.jqId + ' .ui-picklist-button-add-all').click(function() {_self.addAll();});
17961        $(this.jqId + ' .ui-picklist-button-remove').click(function() {_self.remove();});
17962        $(this.jqId + ' .ui-picklist-button-remove-all').click(function() {_self.removeAll();});
17963
17964        if(this.cfg.showSourceControls) {
17965            $(this.jqId + ' .ui-picklist-source-controls .ui-picklist-button-move-up').click(function() {_self.moveUp(_self.sourceList);});
17966            $(this.jqId + ' .ui-picklist-source-controls .ui-picklist-button-move-top').click(function() {_self.moveTop(_self.sourceList);});
17967            $(this.jqId + ' .ui-picklist-source-controls .ui-picklist-button-move-down').click(function() {_self.moveDown(_self.sourceList);});
17968            $(this.jqId + ' .ui-picklist-source-controls .ui-picklist-button-move-bottom').click(function() {_self.moveBottom(_self.sourceList);});
17969        }
17970
17971        if(this.cfg.showTargetControls) {
17972            $(this.jqId + ' .ui-picklist-target-controls .ui-picklist-button-move-up').click(function() {_self.moveUp(_self.targetList);});
17973            $(this.jqId + ' .ui-picklist-target-controls .ui-picklist-button-move-top').click(function() {_self.moveTop(_self.targetList);});
17974            $(this.jqId + ' .ui-picklist-target-controls .ui-picklist-button-move-down').click(function() {_self.moveDown(_self.targetList);});
17975            $(this.jqId + ' .ui-picklist-target-controls .ui-picklist-button-move-bottom').click(function() {_self.moveBottom(_self.targetList);});
17976        }
17977    },
17978
17979    bindFilterEvents: function() {
17980        this.cfg.filterEvent = this.cfg.filterEvent||'keyup';
17981        this.cfg.filterDelay = this.cfg.filterDelay||300;
17982        this.setupFilterMatcher();
17983
17984        this.sourceFilter = $(this.jqId + '_source_filter');
17985        this.targetFilter = $(this.jqId + '_target_filter');
17986
17987        PrimeFaces.skinInput(this.sourceFilter);
17988        this.bindTextFilter(this.sourceFilter);
17989
17990        PrimeFaces.skinInput(this.targetFilter);
17991        this.bindTextFilter(this.targetFilter);
17992    },
17993
17994    bindTextFilter: function(filter) {
17995        if(this.cfg.filterEvent === 'enter')
17996            this.bindEnterKeyFilter(filter);
17997        else
17998            this.bindFilterEvent(filter);
17999    },
18000
18001    bindEnterKeyFilter: function(filter) {
18002        var $this = this;
18003
18004        filter.on('keydown', PrimeFaces.utils.blockEnterKey)
18005        .on('keyup', function(e) {
18006            var key = e.which,
18007            keyCode = $.ui.keyCode;
18008
18009            if((key === keyCode.ENTER)) {
18010                $this.filter(this.value, $this.getFilteredList($(this)));
18011
18012                e.preventDefault();
18013            }
18014        });
18015    },
18016
18017    bindFilterEvent: function(filter) {
18018        var $this = this;
18019
18020        //prevent form submit on enter key
18021        filter.on(this.cfg.filterEvent, function(e) {
18022            if (PrimeFaces.utils.ignoreFilterKey(e)) {
18023                return;
18024            }
18025
18026            var input = $(this);
18027
18028            if($this.filterTimeout) {
18029                clearTimeout($this.filterTimeout);
18030            }
18031
18032            $this.filterTimeout = setTimeout(function() {
18033                $this.filter(input.val(), $this.getFilteredList(input));
18034                $this.filterTimeout = null;
18035            },
18036            $this.cfg.filterDelay);
18037        })
18038        .on('keydown', PrimeFaces.utils.blockEnterKey);
18039    },
18040
18041    setupFilterMatcher: function() {
18042        this.cfg.filterMatchMode = this.cfg.filterMatchMode||'startsWith';
18043        this.filterMatchers = {
18044            'startsWith': this.startsWithFilter
18045            ,'contains': this.containsFilter
18046            ,'endsWith': this.endsWithFilter
18047            ,'custom': this.cfg.filterFunction
18048        };
18049
18050        this.filterMatcher = this.filterMatchers[this.cfg.filterMatchMode];
18051    },
18052
18053    filter: function(value, list) {
18054        var filterValue = $.trim(value).toLowerCase(),
18055        items = list.children('li.ui-picklist-item'),
18056        animated = this.isAnimated();
18057
18058        list.removeAttr('role');
18059        
18060        if(filterValue === '') {
18061            items.filter(':hidden').show();
18062            list.attr('role', 'menu');
18063        }
18064        else {            
18065            for(var i = 0; i < items.length; i++) {
18066                var item = items.eq(i),
18067                itemLabel = item.attr('data-item-label'),
18068                matches = this.filterMatcher(itemLabel, filterValue);
18069
18070                if(matches) {
18071                    var hasRole = list[0].hasAttribute('role');
18072                    if(animated) {
18073                        item.fadeIn('fast', function() {
18074                            if(!hasRole) {
18075                                list.attr('role', 'menu');
18076                            }
18077                        });
18078                    }
18079                    else {
18080                        item.show();
18081                        if(!hasRole) {
18082                            list.attr('role', 'menu');
18083                        }
18084                    }
18085                }
18086                else {
18087                    if(animated) {
18088                        item.fadeOut('fast');
18089                    }
18090                    else {
18091                        item.hide();
18092                    }
18093                }
18094            }
18095        }
18096
18097    },
18098
18099    startsWithFilter: function(value, filter) {
18100        return value.toLowerCase().indexOf(filter) === 0;
18101    },
18102
18103    containsFilter: function(value, filter) {
18104        return value.toLowerCase().indexOf(filter) !== -1;
18105    },
18106
18107    endsWithFilter: function(value, filter) {
18108        return value.indexOf(filter, value.length - filter.length) !== -1;
18109    },
18110
18111    getFilteredList: function(filter) {
18112        return filter.hasClass('ui-source-filter-input') ? this.sourceList : this.targetList;
18113    },
18114
18115    add: function() {
18116        var items = this.sourceList.children('li.ui-picklist-item.ui-state-highlight')
18117
18118        this.transfer(items, this.sourceList, this.targetList, 'command');
18119    },
18120
18121    addAll: function() {
18122        var items = this.sourceList.children('li.ui-picklist-item:visible:not(.ui-state-disabled)');
18123
18124        this.transfer(items, this.sourceList, this.targetList, 'command');
18125    },
18126
18127    remove: function() {
18128        var items = this.targetList.children('li.ui-picklist-item.ui-state-highlight');
18129
18130        this.transfer(items, this.targetList, this.sourceList, 'command');
18131    },
18132
18133    removeAll: function() {
18134        var items = this.targetList.children('li.ui-picklist-item:visible:not(.ui-state-disabled)');
18135
18136        this.transfer(items, this.targetList, this.sourceList, 'command');
18137    },
18138
18139    moveUp: function(list) {
18140        var _self = this,
18141        animated = _self.isAnimated(),
18142        items = list.children('.ui-state-highlight'),
18143        itemsCount = items.length,
18144        movedCount = 0;
18145
18146        if(itemsCount) {
18147            items.each(function() {
18148                var item = $(this);
18149
18150                if(!item.is(':first-child')) {
18151
18152                    if(animated) {
18153                        item.hide(_self.cfg.effect, {}, _self.cfg.effectSpeed, function() {
18154                            item.insertBefore(item.prev()).show(_self.cfg.effect, {}, _self.cfg.effectSpeed, function() {
18155                                movedCount++;
18156
18157                                if(movedCount === itemsCount) {
18158                                    _self.saveState();
18159                                    _self.fireReorderEvent();
18160                                }
18161                            });
18162                        });
18163                    }
18164                    else {
18165                        item.hide().insertBefore(item.prev()).show();
18166                    }
18167
18168                }
18169            });
18170
18171            if(!animated) {
18172                this.saveState();
18173                this.fireReorderEvent();
18174            }
18175        }
18176
18177    },
18178
18179    moveTop: function(list) {
18180        var _self = this,
18181        animated = _self.isAnimated(),
18182        items = list.children('.ui-state-highlight'),
18183        itemsCount = items.length,
18184        movedCount = 0;
18185
18186        if(itemsCount) {
18187            items.each(function() {
18188                var item = $(this);
18189
18190                if(!item.is(':first-child')) {
18191
18192                    if(animated) {
18193                        item.hide(_self.cfg.effect, {}, _self.cfg.effectSpeed, function() {
18194                            item.prependTo(item.parent()).show(_self.cfg.effect, {}, _self.cfg.effectSpeed, function(){
18195                                movedCount++;
18196
18197                                if(movedCount === itemsCount) {
18198                                    _self.saveState();
18199                                    _self.fireReorderEvent();
18200                                }
18201                            });
18202                        });
18203                    }
18204                    else {
18205                        item.hide().prependTo(item.parent()).show();
18206                    }
18207                }
18208            });
18209
18210            if(!animated) {
18211                this.saveState();
18212                this.fireReorderEvent();
18213            }
18214        }
18215    },
18216
18217    moveDown: function(list) {
18218        var _self = this,
18219        animated = _self.isAnimated(),
18220        items = list.children('.ui-state-highlight'),
18221        itemsCount = items.length,
18222        movedCount = 0;
18223
18224        if(itemsCount) {
18225            $(items.get().reverse()).each(function() {
18226                var item = $(this);
18227
18228                if(!item.is(':last-child')) {
18229                    if(animated) {
18230                        item.hide(_self.cfg.effect, {}, _self.cfg.effectSpeed, function() {
18231                            item.insertAfter(item.next()).show(_self.cfg.effect, {}, _self.cfg.effectSpeed, function() {
18232                                movedCount++;
18233
18234                                if(movedCount === itemsCount) {
18235                                    _self.saveState();
18236                                    _self.fireReorderEvent();
18237                                }
18238                            });
18239                        });
18240                    }
18241                    else {
18242                        item.hide().insertAfter(item.next()).show();
18243                    }
18244                }
18245
18246            });
18247
18248            if(!animated) {
18249                this.saveState();
18250                this.fireReorderEvent();
18251            }
18252        }
18253    },
18254
18255    moveBottom: function(list) {
18256        var _self = this,
18257        animated = _self.isAnimated(),
18258        items = list.children('.ui-state-highlight'),
18259        itemsCount = items.length,
18260        movedCount = 0;
18261
18262        if(itemsCount) {
18263            items.each(function() {
18264                var item = $(this);
18265
18266                if(!item.is(':last-child')) {
18267
18268                    if(animated) {
18269                        item.hide(_self.cfg.effect, {}, _self.cfg.effectSpeed, function() {
18270                            item.appendTo(item.parent()).show(_self.cfg.effect, {}, _self.cfg.effectSpeed, function() {
18271                                movedCount++;
18272
18273                                if(movedCount === itemsCount) {
18274                                    _self.saveState();
18275                                    _self.fireReorderEvent();
18276                                }
18277                            });
18278                        });
18279                    }
18280                    else {
18281                        item.hide().appendTo(item.parent()).show();
18282                    }
18283                }
18284
18285            });
18286
18287            if(!animated) {
18288                this.saveState();
18289                this.fireReorderEvent();
18290            }
18291        }
18292    },
18293
18294    /**
18295     * Clear inputs and repopulate them from the list states
18296     */
18297    saveState: function() {
18298        this.sourceInput.children().remove();
18299        this.targetInput.children().remove();
18300
18301        this.generateItems(this.sourceList, this.sourceInput);
18302        this.generateItems(this.targetList, this.targetInput);
18303        this.cursorItem = null;
18304    },
18305
18306    transfer: function(items, from, to, type) {
18307        $(this.jqId + ' ul').sortable('disable');
18308        var $this = this;
18309        var itemsCount = items.length;
18310        var transferCount = 0;
18311
18312        if(this.isAnimated()) {
18313            items.hide(this.cfg.effect, {}, this.cfg.effectSpeed, function() {
18314                var item = $(this);
18315                $this.unselectItem(item);
18316
18317                item.appendTo(to).show($this.cfg.effect, {}, $this.cfg.effectSpeed, function() {
18318                    transferCount++;
18319
18320                    //fire transfer when all items are transferred
18321                    if(transferCount == itemsCount) {
18322                        $this.saveState();
18323                        $this.fireTransferEvent(items, from, to, type);
18324                    }
18325                });
18326                
18327                $this.updateListRole();
18328            });
18329        }
18330        else {
18331            items.hide();
18332
18333            if(this.cfg.showCheckbox) {
18334                items.each(function() {
18335                    $this.unselectItem($(this));
18336                });
18337            }
18338
18339            items.appendTo(to).show();
18340
18341            this.saveState();
18342            this.fireTransferEvent(items, from, to, type);
18343            this.updateListRole();
18344        }
18345    },
18346
18347    /**
18348     * Fire transfer ajax behavior event
18349     */
18350    fireTransferEvent: function(items, from, to, type) {
18351        if(this.cfg.onTransfer) {
18352            var obj = {};
18353            obj.items = items;
18354            obj.from = from;
18355            obj.to = to;
18356            obj.type = type;
18357
18358            this.cfg.onTransfer.call(this, obj);
18359        }
18360
18361        if(this.hasBehavior('transfer')) {
18362            var ext = {
18363                params: []
18364            },
18365            paramName = this.id + '_transferred',
18366            isAdd = from.hasClass('ui-picklist-source');
18367
18368            items.each(function(index, item) {
18369                ext.params.push({name:paramName, value:$(item).attr('data-item-value')});
18370            });
18371
18372            ext.params.push({name:this.id + '_add', value:isAdd});
18373
18374            this.callBehavior('transfer', ext);
18375        }
18376        $(this.jqId + ' ul').sortable('enable');
18377
18378        this.updateButtonsState();
18379    },
18380
18381    getListName: function(element){
18382        return element.parent().hasClass("ui-picklist-source") ? "source" : "target";
18383    },
18384
18385    fireItemSelectEvent: function(item) {
18386        if(this.hasBehavior('select')) {
18387            var listName = this.getListName(item),
18388            inputContainer = (listName === "source") ? this.sourceInput : this.targetInput,
18389            ext = {
18390                params: [
18391                    {name: this.id + '_itemIndex', value: item.index()},
18392                    {name: this.id + '_listName', value: listName}
18393                ],
18394                onstart: function() {
18395                    if(!inputContainer.children().length) {
18396                        return false;
18397                    }
18398                }
18399            };
18400
18401            this.callBehavior('select', ext);
18402        }
18403    },
18404
18405    fireItemUnselectEvent: function(item) {
18406        if(this.hasBehavior('unselect')) {
18407            var ext = {
18408                params: [
18409                    {name: this.id + '_itemIndex', value: item.index()},
18410                    {name: this.id + '_listName', value: this.getListName(item)}
18411                ]
18412            };
18413
18414            this.callBehavior('unselect', ext);
18415        }
18416    },
18417
18418    fireReorderEvent: function() {
18419        this.callBehavior('reorder');
18420    },
18421
18422    isAnimated: function() {
18423        return (this.cfg.effect && this.cfg.effect != 'none');
18424    },
18425
18426    setTabIndex: function() {
18427        var tabindex = (this.cfg.disabled) ? '-1' : this.getTabIndex();
18428        this.sourceList.attr('tabindex', tabindex);
18429        this.targetList.attr('tabindex', tabindex);
18430        $(this.jqId + ' button').attr('tabindex', tabindex);
18431        $(this.jqId + ' .ui-picklist-filter-container > input').attr('tabindex', tabindex);
18432    },
18433    
18434    getTabIndex: function() {
18435        return this.cfg.tabindex||'0';
18436    },
18437
18438    updateButtonsState: function () {
18439        var addButton = $(this.jqId + ' .ui-picklist-button-add');
18440        var sourceListButtons = $(this.jqId + ' .ui-picklist-source-control
18440s .ui-button');
18441        if (this.sourceList.find('li.ui-state-highlight').length) {
18442            this.enableButton(addButton);
18443            this.enableButton(sourceListButtons);
18444        }
18445        else {
18446            this.disableButton(addButton);
18447            this.disableButton(sourceListButtons);
18448        }
18449
18450        var removeButton = $(this.jqId + ' .ui-picklist-button-remove');
18451        var targetListButtons = $(this.jqId + ' .ui-picklist-target-controls .ui-button');
18452        if (this.targetList.find('li.ui-state-highlight').length) {
18453            this.enableButton(removeButton);
18454            this.enableButton(targetListButtons);
18455        }
18456        else {
18457            this.disableButton(removeButton);
18458            this.disableButton(targetListButtons);
18459        }
18460
18461        var addAllButton = $(this.jqId + ' .ui-picklist-button-add-all');
18462        if (this.sourceList.find('li.ui-picklist-item:not(.ui-state-disabled)').length) {
18463            this.enableButton(addAllButton);
18464            this.sourceList.attr('tabindex', this.getTabIndex());
18465        }
18466        else {
18467            this.disableButton(addAllButton);
18468            this.sourceList.attr('tabindex', '-1');
18469        }
18470
18471        var removeAllButton = $(this.jqId + ' .ui-picklist-button-remove-all');
18472        if (this.targetList.find('li.ui-picklist-item:not(.ui-state-disabled)').length) {
18473            this.enableButton(removeAllButton);
18474            this.targetList.attr('tabindex', this.getTabIndex());
18475        }
18476        else {
18477            this.disableButton(removeAllButton);
18478            this.targetList.attr('tabindex', '-1');
18479        }
18480    },
18481
18482    disableButton: function (button) {
18483        if (button.hasClass('ui-state-focus')) {
18484            button.blur();
18485        }
18486        
18487        button.attr('disabled', 'disabled').addClass('ui-state-disabled');
18488        button.attr('tabindex', '-1');
18489    },
18490
18491    enableButton: function (button) {
18492        button.removeAttr('disabled').removeClass('ui-state-disabled');
18493        button.attr('tabindex', this.getTabIndex());
18494    },
18495
18496    updateListRole: function() {
18497        this.sourceList.children('li:visible').length > 0 ? this.sourceList.attr('role', 'menu') : this.sourceList.removeAttr('role');
18498        this.targetList.children('li:visible').length > 0 ? this.targetList.attr('role', 'menu') : this.targetList.removeAttr('role');
18499    }
18500
18501});
18502/**
18503 * PrimeFaces ProgressBar widget
18504 */
18505PrimeFaces.widget.ProgressBar = PrimeFaces.widget.BaseWidget.extend({
18506
18507    init: function(cfg) {
18508        this._super(cfg);
18509
18510        this.jqValue = this.jq.children('.ui-progressbar-value');
18511        this.jqLabel = this.jq.children('.ui-progressbar-label');
18512        this.value = this.cfg.initialValue;
18513        this.cfg.global = (this.cfg.global === false) ? false : true;
18514
18515        if(this.cfg.ajax) {
18516            this.cfg.formId = this.jq.closest('form').attr('id');
18517        }
18518
18519        this.enableARIA();
18520    },
18521
18522    setValue: function(value) {
18523        if(value >= 0 && value<=100) {
18524            if(value == 0) {
18525                this.jqValue.hide().css('width', '0%').removeClass('ui-corner-right');
18526
18527                this.jqLabel.hide();
18528            }
18529            else {
18530                this.jqValue.show().animate({
18531                    'width': value + '%'
18532                }, this.cfg.animationDuration, 'easeInOutCirc');
18533
18534                if(this.cfg.labelTemplate) {
18535                    var formattedLabel = this.cfg.labelTemplate.replace(/{value}/gi, value);
18536                    this.jqLabel.text(formattedLabel).show();
18537                }
18538            }
18539
18540            this.value = value;
18541            this.jq.attr('aria-valuenow', value);
18542        }
18543    },
18544
18545    getValue: function() {
18546        return this.value;
18547    },
18548
18549    start: function() {
18550        var $this = this;
18551
18552        if(this.cfg.ajax) {
18553
18554            this.progressPoll = setInterval(function() {
18555                var options = {
18556                    source: $this.id,
18557                    process: $this.id,
18558                    formId: $this.cfg.formId,
18559                    global: $this.cfg.global,
18560                    async: true,
18561                    oncomplete: function(xhr, status, args, data) {
18562                        var value = args[$this.id + '_value'];
18563                        $this.setValue(value);
18564
18565                        //trigger complete listener
18566                        if(value === 100) {
18567                            $this.fireCompleteEvent();
18568                        }
18569                    }
18570                };
18571
18572                PrimeFaces.ajax.Request.handle(options);
18573
18574            }, this.cfg.interval);
18575        }
18576    },
18577
18578    fireCompleteEvent: function() {
18579        clearInterval(this.progressPoll);
18580
18581        this.callBehavior('complete');
18582    },
18583
18584    cancel: function() {
18585        clearInterval(this.progressPoll);
18586        this.setValue(0);
18587    },
18588
18589    enableARIA: function() {
18590        this.jq.attr('role', 'progressbar')
18591                .attr('aria-valuemin', 0)
18592                .attr('aria-valuenow', this.value)
18593                .attr('aria-valuemax', 100);
18594    }
18595
18596});
18597/**
18598 * PrimeFaces Rating Widget
18599 */
18600PrimeFaces.widget.Rating = PrimeFaces.widget.BaseWidget.extend({
18601
18602    init: function(cfg) {
18603        this._super(cfg);
18604        this.jqInput = $(this.jqId + '_input');
18605        this.value = this.getValue();
18606        this.stars = this.jq.children('.ui-rating-star');
18607        this.cancel = this.jq.children('.ui-rating-cancel');
18608
18609        if(!this.cfg.disabled && !this.cfg.readonly) {
18610            this.bindEvents();
18611        }
18612
18613        if(this.cfg.readonly) {
18614            this.jq.children().css('cursor', 'default');
18615        }
18616    },
18617
18618    bindEvents: function() {
18619        var _self = this;
18620
18621        this.stars.click(function() {
18622            var value = _self.stars.index(this) + 1;   //index starts from zero
18623
18624            _self.setValue(value);
18625        });
18626
18627        this.cancel.hover(function() {
18628            $(this).toggleClass('ui-rating-cancel-hover');
18629        })
18630        .click(function() {
18631            _self.reset();
18632        });
18633    },
18634
18635    unbindEvents: function() {
18636        this.stars.unbind('click');
18637
18638        this.cancel.unbind('hover click');
18639    },
18640
18641    getValue: function() {
18642        var inputVal = this.jqInput.val();
18643
18644        return inputVal == '' ? null : parseInt(inputVal);
18645    },
18646
18647    setValue: function(value) {
18648        //set hidden value
18649        this.jqInput.val(value);
18650
18651        //update visuals
18652        this.stars.removeClass('ui-rating-star-on');
18653        for(var i = 0; i < value; i++) {
18654            this.stars.eq(i).addClass('ui-rating-star-on');
18655        }
18656
18657        //invoke callback
18658        if(this.cfg.onRate) {
18659            this.cfg.onRate.call(this, value);
18660        }
18661
18662        this.callBehavior('rate');
18663    },
18664
18665    enable: function() {
18666        this.cfg.disabled = false;
18667
18668        this.bindEvents();
18669
18670        this.jq.removeClass('ui-state-disabled');
18671    },
18672
18673    disable: function() {
18674        this.cfg.disabled = true;
18675
18676        this.unbindEvents();
18677
18678        this.jq.addClass('ui-state-disabled');
18679    },
18680
18681    reset: function() {
18682        this.jqInput.val('');
18683
18684        this.stars.filter('.ui-rating-star-on').removeClass('ui-rating-star-on');
18685
18686        this.callBehavior('cancel');
18687    }
18688});
18689/**
18690 * PrimeFaces Resizable Widget
18691 */
18692PrimeFaces.widget.Resizable = PrimeFaces.widget.BaseWidget.extend({
18693
18694    init: function(cfg) {
18695        this.cfg = cfg;
18696        this.id = this.cfg.id;
18697        this.jqId = PrimeFaces.escapeClientId(this.id);
18698        this.jqTarget = $(PrimeFaces.escapeClientId(this.cfg.target));
18699
18700        this.renderDeferred();
18701    },
18702
18703    //@Override
18704    renderDeferred: function() {
18705        if(this.jqTarget.is(':visible')) {
18706            this._render();
18707        }
18708        else {
18709            var container = this.jqTarget.parent()[0].closest('.ui-hidden-container');
18710            if (container) {
18711                var $container = $(container);
18712                if ($container.length) {
18713                    var $this = this;
18714                    PrimeFaces.addDeferredRender(this.id, $container.attr('id'), function() {
18715                        return $this.render();
18716                    });
18717                }
18718            }
18719        }
18720    },
18721
18722    render: function() {
18723        if(this.jqTarget.is(':visible')) {
18724            this._render();
18725            return true;
18726        }
18727
18728        return false;
18729    },
18730
18731    _render: function() {
18732        if(this.cfg.ajaxResize) {
18733            this.cfg.formId = $(this.target).parents('form:first').attr('id');
18734        }
18735
18736        if (this.cfg.isContainment) {
18737        	this.cfg.containment = PrimeFaces.escapeClientId(this.cfg.parentComponentId);
18738        }
18739
18740        var _self = this;
18741
18742        this.cfg.stop = function(event, ui) {
18743            if(_self.cfg.onStop) {
18744                _self.cfg.onStop.call(_self, event, ui);
18745            }
18746
18747            _self.fireAjaxResizeEvent(event, ui);
18748        };
18749
18750        this.cfg.start = function(event, ui) {
18751            if(_self.cfg.onStart) {
18752                _self.cfg.onStart.call(_self, event, ui);
18753            }
18754        };
18755
18756        this.cfg.resize = function(event, ui) {
18757            if(_self.cfg.onResize) {
18758                _self.cfg.onResize.call(_self, event, ui);
18759            }
18760        };
18761
18762        this.jqTarget.resizable(this.cfg);
18763
18764        this.removeScriptElement(this.id);
18765    },
18766
18767    fireAjaxResizeEvent: function(event, ui) {
18768        if(this.hasBehavior('resize')) {
18769            var ext = {
18770                params: [
18771                    {name: this.id + '_width', value: parseInt(ui.helper.width())},
18772                    {name: this.id + '_height', value: parseInt(ui.helper.height())}
18773                ]
18774            };
18775
18776            this.callBehavior('resize', ext);
18777        }
18778    }
18779
18780});
18781/**
18782 * PrimeFaces Slider Widget
18783 */
18784PrimeFaces.widget.Slider = PrimeFaces.widget.BaseWidget.extend({
18785
18786    init: function(cfg) {
18787        this._super(cfg);
18788
18789        this.cfg.displayTemplate = this.cfg.displayTemplate||(this.cfg.range ? '{min} - {max}' : '{value}');
18790
18791        if(this.cfg.range) {
18792            var inputIds = this.cfg.input.split(',');
18793            this.input = $(PrimeFaces.escapeClientId(inputIds[0]) + ',' + PrimeFaces.escapeClientId(inputIds[1]));
18794        }
18795        else {
18796            this.input = $(PrimeFaces.escapeClientId(this.cfg.input));
18797        }
18798
18799        if(this.cfg.display) {
18800            this.output = $(PrimeFaces.escapeClientId(this.cfg.display));
18801        }
18802
18803        this.jq.slider(this.cfg);
18804
18805        this.decimalStep = !(this.cfg.step % 1 === 0);
18806
18807        this.bindEvents();
18808
18809        if (PrimeFaces.env.touch) {
18810            this.bindTouchEvents();
18811        }
18812    },
18813
18814    bindEvents: function() {
18815        var $this = this;
18816
18817        this.jq.on('slide', function(event, ui) {
18818            $this.onSlide(event, ui);
18819        });
18820
18821        if(this.cfg.onSlideStart) {
18822            this.jq.on('slidestart', function(event, ui) {
18823                $this.cfg.onSlideStart.call(this, event, ui);
18824            });
18825        }
18826
18827        this.jq.on('slidestop', function(event, ui) {
18828            $this.onSlideEnd(event, ui);
18829        });
18830
18831        if (this.input.parent().hasClass('ui-inputnumber')) {
18832            this.input.parent().find('input:hidden').on('change', function () {
18833                $this.setValue($(this).val());
18834            });
18835        }
18836        else {
18837            this.input.on('keydown.slider', function (e) {
18838                var keyCode = $.ui.keyCode,
18839                key = e.which;
18840
18841                switch(key) {
18842                    case keyCode.UP:
18843                    case keyCode.DOWN:
18844                    case keyCode.LEFT:
18845                    case keyCode.RIGHT:
18846                    case keyCode.BACKSPACE:
18847                    case keyCode.DELETE:
18848                    case keyCode.END:
18849                    case keyCode.HOME:
18850                    case keyCode.TAB:
18851                    break;
18852
18853                    default:
18854                        var metaKey = e.metaKey||e.ctrlKey,
18855                        isNumber = (key >= 48 && key <= 57) || (key >= 96 && key <= 105) || (key === 190);
18856
18857                        //prevent special characters with alt and shift
18858                        if(e.altKey || (e.shiftKey && !(key === keyCode.UP || key === keyCode.DOWN || key === keyCode.LEFT || key === keyCode.RIGHT))) {
18859                            e.preventDefault();
18860                        }
18861
18862                        //prevent letters and allow letters with meta key such as ctrl+c
18863                        //also allow '.' when the step is defined as decimal
18864                        if(!isNumber && !metaKey && !($this.decimalStep && key === 190)) {
18865                            e.preventDefault();
18866                        }
18867                    break;
18868                }
18869            }).on('keyup.slider', function (e) {
18870                $this.setValue($this.input.val());
18871            });
18872        }
18873    },
18874
18875    bindTouchEvents: function() {
18876        var eventMapping = {
18877            touchstart: 'mousedown',
18878            touchmove: 'mousemove',
18879            touchend: 'mouseup'
18880        };
18881
18882        this.jq.children('.ui-slider-handle').on('touchstart touchmove touchend', function(e) {
18883            var touch = e.originalEvent.changedTouches[0];
18884            var targetEvent = document.createEvent('MouseEvent');
18885
18886            targetEvent.initMouseEvent(
18887                    eventMapping[e.originalEvent.type],
18888                    true, // canBubble
18889                    true, // cancelable
18890                    window, // view
18891                    1, // detail
18892                    touch.screenX,
18893                    touch.screenY,
18894                    touch.clientX,
18895                    touch.clientY,
18896                    false, // ctrlKey
18897                    false, // altKey
18898                    false, // shiftKey
18899                    false, // metaKey
18900                    0, // button
18901                    null // relatedTarget
18902                );
18903
18904            touch.target.dispatchEvent(targetEvent);
18905            e.preventDefault();
18906        });
18907    },
18908
18909    onSlide: function(event, ui) {
18910        if(this.cfg.onSlide) {
18911            this.cfg.onSlide.call(this, event, ui);
18912        }
18913
18914        if(this.cfg.range) {
18915            this.setInputValue(this.input.eq(0), ui.values[0]);
18916            this.setInputValue(this.input.eq(1), ui.values[1]);
18917
18918            if(this.output) {
18919                this.output.text(this.cfg.displayTemplate.replace('{min}', ui.values[0]).replace('{max}', ui.values[1]));
18920            }
18921        }
18922        else {
18923            this.setInputValue(this.input, ui.value);
18924
18925            if(this.output) {
18926                this.output.text(this.cfg.displayTemplate.replace('{value}', ui.value));
18927            }
18928        }
18929    },
18930
18931    setInputValue: function(input, inputValue) {
18932        if (input.parent().hasClass('ui-inputnumber')) {
18933            var inputNumberId = input.closest('.ui-inputnumber').attr('id');
18934            var inputNumberWidget = PrimeFaces.getWidgetById(inputNumberId);
18935            inputNumberWidget.autonumeric.set(inputValue);
18936        }
18937        else if (input.hasClass('ui-spinner-input')) {
18938            var spinnerId = input.closest('.ui-spinner').attr('id');
18939            var spinnerWidget = PrimeFaces.getWidgetById(spinnerId);
18940            spinnerWidget.setValue(inputValue);
18941        }
18942        else {
18943            input.val(inputValue);
18944        }
18945    },
18946
18947    triggerOnchange: function(input) {
18948        if (input.parent().hasClass('ui-inputnumber')) {
18949            input.change();
18950        }
18951        else if (input.hasClass('ui-spinner-input')) {
18952            input.change();
18953        }
18954    },
18955
18956    onSlideEnd: function(event, ui) {
18957        if(this.cfg.onSlideEnd) {
18958            this.cfg.onSlideEnd.call(this, event, ui);
18959        }
18960
18961        if(this.cfg.range) {
18962            this.triggerOnchange(this.input.eq(0));
18963            this.triggerOnchange(this.input.eq(1));
18964        }
18965        else {
18966            this.triggerOnchange(this.input);
18967        }
18968
18969        if(this.hasBehavior('slideEnd')) {
18970            var ext = {
18971                params: [
18972                    {name: this.id + '_slideValue', value: ui.value}
18973                ]
18974            };
18975
18976            this.callBehavior('slideEnd', ext);
18977        }
18978    },
18979
18980    getValue: function() {
18981        return this.jq.slider('value');
18982    },
18983
18984    setValue: function(value) {
18985        this.jq.slider('value', value);
18986    },
18987
18988    getValues: function() {
18989        return this.jq.slider('values');
18990    },
18991
18992    setValues: function(values) {
18993        this.jq.slider('values', values);
18994    },
18995
18996    enable: function() {
18997        this.jq.slider('enable');
18998    },
18999
19000    disable: function() {
19001        this.jq.slider('disable');
19002    }
19003});
19004/**
19005 * PrimeFaces Spinner Widget
19006 */
19007PrimeFaces.widget.Spinner = PrimeFaces.widget.BaseWidget.extend({
19008
19009    init: function(cfg) {
19010        this._super(cfg);
19011
19012        this.input = this.jq.children('.ui-spinner-input');
19013        this.upButton = this.jq.children('a.ui-spinner-up');
19014        this.downButton = this.jq.children('a.ui-spinner-down');
19015        this.cfg.step = this.cfg.step || 1;
19016        if (this.cfg.thousandSeparator == undefined) {
19017          this.cfg.thousandSeparator = '';
19018        }
19019        if (!this.cfg.decimalSeparator) {
19020          this.cfg.decimalSeparator = '.';
19021        }
19022        this.cursorOffset = this.cfg.prefix ? this.cfg.prefix.length: 0;
19023
19024        var inputValue = this.input.val();
19025
19026        if(this.cfg.decimalPlaces > 0) {
19027            this.cfg.precision = this.cfg.decimalPlaces;
19028        }
19029        else if(!(typeof this.cfg.step === 'number' && this.cfg.step % 1 === 0)) {
19030            this.cfg.precision = this.cfg.step.toString().split(/[,]|[.]/)[1].length;
19031        }
19032
19033        var maxlength = this.input.attr('maxlength');
19034        if(maxlength) {
19035            this.cfg.maxlength = parseInt(maxlength);
19036        }
19037
19038        this.value = this.parseValue(inputValue);
19039
19040        this.format();
19041
19042        this.addARIA();
19043
19044        if(this.input.prop('disabled')||this.input.prop('readonly')) {
19045            return;
19046        }
19047
19048        this.bindEvents();
19049
19050        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
19051
19052        PrimeFaces.skinInput(this.input);
19053    },
19054
19055    bindEvents: function() {
19056        var $this = this;
19057
19058        this.jq.children('.ui-spinner-button')
19059            .on('mouseover.spinner', function() {
19060                $(this).addClass('ui-state-hover');
19061            })
19062            .on('mouseout.spinner', function() {
19063                $(this).removeClass('ui-state-hover ui-state-active');
19064
19065                if($this.timer) {
19066                    clearInterval($this.timer);
19067                }
19068            })
19069            .on('mouseup.spinner', function() {
19070                clearInterval($this.timer);
19071                $(this).removeClass('ui-state-active').addClass('ui-state-hover');
19072                $this.input.trigger('change');
19073            })
19074            .on('mousedown.spinner', function(e) {
19075                var element = $(this),
19076                dir = element.hasClass('ui-spinner-up') ? 1 : -1;
19077
19078                element.removeClass('ui-state-hover').addClass('ui-state-active');
19079
19080                if($this.input.is(':not(:focus)')) {
19081                    $this.input.focus();
19082                }
19083
19084                $this.repeat(null, dir);
19085
19086                //keep focused
19087                e.preventDefault();
19088        });
19089
19090        this.input.on('keydown.spinner', function (e) {
19091            var keyCode = $.ui.keyCode;
19092
19093            switch(e.which) {
19094                case keyCode.UP:
19095                    $this.spin(1);
19096                break;
19097
19098                case keyCode.DOWN:
19099                    $this.spin(-1);
19100                break;
19101
19102                case keyCode.ENTER:
19103                    $this.updateValue();
19104                    $this.format();
19105                break;
19106
19107                default:
19108                    //do nothing
19109                break;
19110            }
19111
19112            /* Github #1964 do not allow minus */
19113            if ($this.cfg.min >= 0 && event.key === "-") {
19114                e.preventDefault();
19115            }
19116        })
19117        .on('keyup.spinner', function (e) {
19118            $this.updateValue();
19119
19120            var keyCode = $.ui.keyCode;
19121
19122            /* Github #2636 */
19123            var checkForIE = (PrimeFaces.env.isIE(11) || PrimeFaces.env.isLtIE(11)) && (e.which === keyCode.ENTER);
19124
19125            if(e.which === keyCode.UP||e.which === keyCode.DOWN||checkForIE) {
19126                $this.input.trigger('change');
19127                $this.format();
19128            }
19129        })
19130        .on('blur.spinner', function(e) {
19131            $this.format();
19132        })
19133        .on('mousewheel.spinner', function(event, delta) {
19134            if($this.input.is(':focus')) {
19135                if(delta > 0)
19136                    $this.spin(1);
19137                else
19138                    $this.spin(-1);
19139                
19140                $this.input.trigger('change');
19141
19142                return false;
19143            }
19144        });
19145    },
19146
19147    repeat: function(interval, dir) {
19148        var $this = this,
19149        i = interval||500;
19150
19151        clearTimeout(this.timer);
19152        this.timer = setTimeout(function() {
19153            $this.repeat(40, dir);
19154        }, i);
19155
19156        this.spin(dir);
19157    },
19158
19159    spin: function(dir) {
19160        var step = this.cfg.step * dir,
19161        currentValue = this.value ? this.value : 0,
19162        newValue = this.parseValue(currentValue + step);
19163
19164        if(this.cfg.maxlength !== undefined && newValue.toString().length > this.cfg.maxlength) {
19165            newValue = currentValue;
19166        }
19167
19168        this.value = newValue;
19169        this.format();
19170        this.input.attr('aria-valuenow', this.getValue());
19171    },
19172
19173    updateValue: function() {
19174        var value = this.input.val();
19175
19176        if(this.cfg.prefix && value.indexOf(this.cfg.prefix) === 0) {
19177            value = value.substring(this.cfg.prefix.length, value.length);
19178        }  else {
19179            var ix = value.indexOf(this.cfg.suffix);
19180            if(this.cfg.suffix && ix > -1 && ix === (value.length - this.cfg.suffix.length)) {
19181                value = value.substring(0, value.length - this.cfg.suffix.length);
19182            }
19183        }
19184
19185        value = value.replace(new RegExp(PrimeFaces.escapeRegExp(this.cfg.thousandSeparator), 'g'), '');
19186        value = value.replace(new RegExp(PrimeFaces.escapeRegExp(this.cfg.decimalSeparator), 'g'), '\.');
19187        this.value = this.parseValue(value);
19188    },
19189
19190    parseValue: function(value) {
19191        var parsedValue;
19192        if(this.cfg.precision) {
19193            parsedValue = parseFloat(value);
19194        } else {
19195            parsedValue = parseInt(value);
19196        }
19197        if(isNaN(parsedValue)) {
19198            if($.trim(value) === '' && this.cfg.min !== undefined && this.cfg.required) {
19199                parsedValue = this.cfg.min;
19200            } else {
19201                parsedValue = null;
19202            }
19203        } else {
19204            var minimum = this.cfg.min;
19205            var maximum = this.cfg.max;
19206
19207            if (this.cfg.rotate) {
19208                if(parsedValue < minimum) {
19209                    parsedValue = maximum;
19210                }
19211                if(parsedValue > maximum) {
19212                    parsedValue = minimum;
19213                }
19214            } else {
19215                if(parsedValue > maximum) {
19216                    parsedValue = maximum;
19217                }
19218                if(parsedValue < minimum) {
19219                    parsedValue = minimum;
19220                }
19221            }
19222        }
19223        return parsedValue;
19224    },
19225
19226    format: function() {
19227        if(this.value !== null) {
19228            var value = this.getValue();
19229            var numAndFract = value.toString().split('.');
19230            value = numAndFract[0].replace(/(\d)(?=(?:\d{3})+\b)/g, '$1' + this.cfg.thousandSeparator);
19231            if (numAndFract.length === 2) {
19232              value += this.cfg.decimalSeparator + numAndFract[1];
19233            }
19234            if(this.cfg.prefix)
19235                value = this.cfg.prefix + value;
19236
19237            if(this.cfg.suffix)
19238                value = value + this.cfg.suffix;
19239
19240            this.input.val(value);
19241        }
19242    },
19243
19244
19245    addARIA: function() {
19246        this.input.attr('role', 'spinbutton');
19247        this.input.attr('aria-multiline', false);
19248        this.input.attr('aria-valuenow', this.getValue());
19249
19250        if(this.cfg.min !== undefined)
19251            this.input.attr('aria-valuemin', this.cfg.min);
19252
19253        if(this.cfg.max !== undefined)
19254            this.input.attr('aria-valuemax', this.cfg.max);
19255
19256        if(this.input.prop('disabled'))
19257            this.input.attr('aria-disabled', true);
19258
19259        if(this.input.prop('readonly'))
19260            this.input.attr('aria-readonly', true);
19261    },
19262
19263    getValue: function() {
19264        if(this.cfg.precision) {
19265            return parseFloat(this.value).toFixed(this.cfg.precision);
19266        }
19267        else {
19268            return this.value;
19269        }
19270    },
19271
19272    setValue: function(value) {
19273        this.value = value;
19274        this.format();
19275    }
19276});
19277
19278/**
19279 * PrimeFaces Spotlight Widget
19280 */
19281PrimeFaces.widget.Spotlight = PrimeFaces.widget.BaseWidget.extend({
19282
19283    init: function(cfg) {
19284        this._super(cfg);
19285        this.target = PrimeFaces.expressions.SearchExpressionFacade.resolve
19285ComponentsAsSelector(this.cfg.target);
19286
19287        if(!$(document.body).children('.ui-spotlight').length) {
19288            this.createMasks();
19289        }
19290
19291        if(this.cfg.active) {
19292            this.show();
19293        }
19294    },
19295
19296    createMasks: function() {
19297        var documentBody = $(document.body);
19298        documentBody.append('<div class="ui-widget-overlay ui-spotlight ui-spotlight-top ui-helper-hidden"></div><div class="ui-widget-overlay ui-spotlight ui-spotlight-bottom ui-helper-hidden"></div>' +
19299                        '<div class="ui-widget-overlay ui-spotlight ui-spotlight-left ui-helper-hidden"></div><div class="ui-widget-overlay ui-spotlight ui-spotlight-right ui-helper-hidden"></div>');
19300    },
19301
19302    show: function() {
19303        this.calculatePositions();
19304
19305        $(document.body).children('div.ui-spotlight').show();
19306
19307        this.bindEvents();
19308    },
19309
19310    calculatePositions: function() {
19311        var doc = $(document),
19312        documentBody = $(document.body),
19313        offset = PrimeFaces.utils.calculateRelativeOffset(this.target),
19314        zindex = ++PrimeFaces.zindex;
19315
19316        documentBody.children('div.ui-spotlight-top').css({
19317            'left': 0,
19318            'top': 0,
19319            'width': documentBody.width(),
19320            'height': offset.top,
19321            'z-index': zindex
19322        });
19323
19324        var bottomTop = offset.top + this.target.outerHeight();
19325        documentBody.children('div.ui-spotlight-bottom').css({
19326            'left': 0,
19327            'top': bottomTop,
19328            'width': documentBody.width(),
19329            'height': doc.height() - bottomTop,
19330            'z-index': zindex
19331        });
19332
19333        documentBody.children('div.ui-spotlight-left').css({
19334            'left': 0,
19335            'top': offset.top,
19336            'width': offset.left,
19337            'height': this.target.outerHeight(),
19338            'z-index': zindex
19339        });
19340
19341        var rightLeft = offset.left + this.target.outerWidth();
19342        documentBody.children('div.ui-spotlight-right').css({
19343            'left': rightLeft,
19344            'top': offset.top,
19345            'width': documentBody.width() - rightLeft,
19346            'height': this.target.outerHeight(),
19347            'z-index': zindex
19348        });
19349    },
19350
19351    bindEvents: function() {
19352        var $this = this;
19353
19354        this.target.data('zindex',this.target.zIndex()).css('z-index', ++PrimeFaces.zindex);
19355
19356        if (this.cfg.blockScroll) {
19357            PrimeFaces.utils.preventScrolling();
19358        }
19359        PrimeFaces.utils.preventTabbing(this.id, $this.target.zIndex(), function() {
19360            return $this.target.find(':tabbable');
19361        });
19362
19363        $(window).on('resize.spotlight scroll.spotlight', function() {
19364            $this.calculatePositions();
19365        });
19366    },
19367
19368    unbindEvents: function() {
19369        PrimeFaces.utils.enableTabbing(this.id);
19370        if (this.cfg.blockScroll) {
19371            PrimeFaces.utils.enableScrolling();
19372        }
19373        $(window).off('resize.spotlight scroll.spotlight');
19374    },
19375
19376    hide: function() {
19377        $(document.body).children('.ui-spotlight').hide();
19378        this.unbindEvents();
19379        this.target.css('z-index', this.target.zIndex());
19380    }
19381
19382});
19383/**
19384 * PrimeFaces Sticky Widget
19385 */
19386PrimeFaces.widget.Sticky = PrimeFaces.widget.BaseWidget.extend({
19387
19388    init: function(cfg) {
19389        this._super(cfg);
19390        this.target = $(PrimeFaces.escapeClientId(this.cfg.target));
19391        this.cfg.margin = this.cfg.margin||0;
19392
19393        this.initialState = {
19394            top: this.target.offset().top,
19395            height: this.target.height()
19396        };
19397
19398        this.bindEvents();
19399    },
19400
19401    refresh: function(cfg) {
19402        this.target = $(PrimeFaces.escapeClientId(this.cfg.target));
19403
19404        if(this.fixed) {
19405            this.ghost.remove();
19406            this.fix(true);
19407        }
19408    },
19409
19410    bindEvents: function() {
19411        var $this = this,
19412        win = $(window);
19413
19414        PrimeFaces.utils.registerScrollHandler(this, 'scroll.' + this.id + '_align', function() {
19415            if(win.scrollTop() > $this.initialState.top - $this.cfg.margin)
19416                $this.fix();
19417            else
19418                $this.restore();
19419        });
19420
19421        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', null, function() {
19422            if ($this.fixed) {
19423                $this.target.width($this.ghost.outerWidth() - ($this.target.outerWidth() - $this.target.width()));
19424            }
19425        });
19426    },
19427
19428    fix: function(force) {
19429        if(!this.fixed || force) {
19430            var win = $(window),
19431            winScrollTop = win.scrollTop();
19432
19433            this.target.css({
19434                'position': 'fixed',
19435                'top': this.cfg.margin,
19436                'z-index': ++PrimeFaces.zindex
19437            })
19438            .addClass('ui-shadow ui-sticky');
19439
19440            this.ghost = $('<div class="ui-sticky-ghost"></div>').height(this.target.outerHeight()).insertBefore(this.target);
19441            this.target.width(this.ghost.outerWidth() - (this.target.outerWidth() - this.target.width()));
19442            this.fixed = true;
19443            win.scrollTop(winScrollTop);
19444        }
19445    },
19446
19447    restore: function() {
19448        if(this.fixed) {
19449            this.target.css({
19450                position: 'static',
19451                top: 'auto',
19452                width: 'auto'
19453            })
19454            .removeClass('ui-shadow ui-sticky');
19455
19456            this.ghost.remove();
19457            this.fixed = false;
19458        }
19459    }
19460
19461});
19462
19463/**
19464 * PrimeFaces TabView Widget
19465 */
19466PrimeFaces.widget.TabView = PrimeFaces.widget.DeferredWidget.extend({
19467
19468    init: function(cfg) {
19469        this._super(cfg);
19470
19471        this.panelContainer = this.jq.children('.ui-tabs-panels');
19472        this.stateHolder = $(this.jqId + '_activeIndex');
19473        this.cfg.selected = parseInt(this.stateHolder.val());
19474        this.focusedTabHeader = null;
19475        this.tabindex = this.cfg.tabindex||0;
19476
19477        if(this.cfg.scrollable) {
19478            this.navscroller = this.jq.children('.ui-tabs-navscroller');
19479            this.navcrollerLeft = this.navscroller.children('.ui-tabs-navscroller-btn-left');
19480            this.navcrollerRight = this.navscroller.children('.ui-tabs-navscroller-btn-right');
19481            this.navContainer = this.navscroller.children('.ui-tabs-nav');
19482            this.firstTab = this.navContainer.children('li.ui-tabs-header:first-child');
19483            this.lastTab = this.navContainer.children('li.ui-tabs-header:last-child');
19484            this.scrollStateHolder = $(this.jqId + '_scrollState');
19485        }
19486        else {
19487            this.navContainer = this.jq.children('.ui-tabs-nav');
19488        }
19489
19490        this.headerContainer = this.navContainer.children('li.ui-tabs-header');
19491
19492        this.bindEvents();
19493
19494        //Cache initial active tab
19495        if(this.cfg.dynamic && this.cfg.cache) {
19496            this.markAsLoaded(this.panelContainer.children().eq(this.cfg.selected));
19497        }
19498
19499        this.renderDeferred();
19500    },
19501
19502    //@Override
19503    renderDeferred: function() {
19504        if(this.jq.is(':visible')) {
19505            this._render();
19506        }
19507        else {
19508            var container = this.jq.parent()[0].closest('.ui-hidden-container');
19509            if (container) {
19510                var $container = $(container);
19511                if ($container.length) {
19512                    var $this = this;
19513                    this.addDeferredRender(this.id, $container, function() {
19514                        return $this.render();
19515                    });
19516                }
19517            }
19518        }
19519    },
19520
19521    _render: function() {
19522        if(this.cfg.scrollable) {
19523            this.initScrolling();
19524
19525            var $this = this;
19526
19527            PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', null, function() {
19528                $this.initScrolling();
19529            });
19530        }
19531    },
19532
19533    bindEvents: function() {
19534        var $this = this;
19535
19536        //Tab header events
19537        this.headerContainer
19538                .on('mouseover.tabview', function(e) {
19539                    var element = $(this);
19540                    if(!element.hasClass('ui-state-disabled')) {
19541                        element.addClass('ui-state-hover');
19542                    }
19543                })
19544                .on('mouseout.tabview', function(e) {
19545                    var element = $(this);
19546                    if(!element.hasClass('ui-state-disabled')) {
19547                        element.removeClass('ui-state-hover');
19548                    }
19549                })
19550                .on('click.tabview', function(e) {
19551                    var element = $(this);
19552
19553                    if($(e.target).is(':not(.ui-icon-close)')) {
19554                        var index = $this.headerContainer.index(element);
19555
19556                        if(!element.hasClass('ui-state-disabled') && index !== $this.cfg.selected) {
19557                            $this.select(index);
19558                        }
19559                    }
19560
19561                    e.preventDefault();
19562                });
19563
19564        //Closable tabs
19565        this.navContainer.find('li .ui-icon-close')
19566            .on('click.tabview', function(e) {
19567                var index = $(this).parent().index();
19568
19569                if($this.cfg.onTabClose) {
19570                    var retVal = $this.cfg.onTabClose.call($this, index);
19571
19572                    if(retVal !== false) {
19573                        $this.remove(index);
19574                    }
19575                }
19576                else {
19577                    $this.remove(index);
19578                }
19579
19580                e.preventDefault();
19581            });
19582
19583        //Scrolling
19584        if(this.cfg.scrollable) {
19585            this.navscroller.children('.ui-tabs-navscroller-btn')
19586                            .on('mouseover.tabview', function() {
19587                                var el = $(this);
19588                                if(!el.hasClass('ui-state-disabled'))
19589                                    $(this).addClass('ui-state-hover');
19590                            })
19591                            .on('mouseout.tabview', function() {
19592                                var el = $(this);
19593                                if(!el.hasClass('ui-state-disabled'))
19594                                    $(this).removeClass('ui-state-hover ui-state-active');
19595                            })
19596                            .on('mousedown.tabview', function() {
19597                                var el = $(this);
19598                                if(!el.hasClass('ui-state-disabled'))
19599                                    $(this).removeClass('ui-state-hover').addClass('ui-state-active');
19600                            })
19601                            .on('mouseup.tabview', function() {
19602                                var el = $(this);
19603                                if(!el.hasClass('ui-state-disabled'))
19604                                    $(this).addClass('ui-state-hover').removeClass('ui-state-active');
19605                            })
19606                            .on('focus.tabview', function() {
19607                                $(this).addClass('ui-state-focus');
19608                            })
19609                            .on('blur.tabview', function() {
19610                                $(this).removeClass('ui-state-focus');
19611                            });
19612
19613
19614            this.navcrollerLeft.on('click.tabview', function(e) {
19615                                $this.scroll(100);
19616                                e.preventDefault();
19617                            });
19618
19619            this.navcrollerRight.on('click.tabview', function(e) {
19620                                $this.scroll(-100);
19621                                e.preventDefault();
19622                            });
19623        }
19624
19625        this.bindSwipeEvents();
19626        this.bindKeyEvents();
19627    },
19628
19629    /**
19630     * Binds swipe events to this tabview.
19631     */
19632    bindSwipeEvents: function() {
19633        if (!PrimeFaces.env.touch) {
19634            return;
19635        }
19636        var $this = this;
19637        this.jq.swipe({
19638            swipeLeft:function(event) {
19639                var activeIndex = $this.getActiveIndex();
19640                if (activeIndex < $this.getLength() - 1) {
19641                    $this.select(activeIndex + 1);
19642                }
19643            },
19644            swipeRight: function(event) {
19645                var activeIndex = $this.getActiveIndex();
19646                if (activeIndex > 0) {
19647                    $this.select(activeIndex - 1);
19648                }
19649            },
19650            excludedElements: PrimeFaces.utils.excludedSwipeElements()
19651        });
19652    },
19653
19654    bindKeyEvents: function() {
19655        var $this = this,
19656            tabs = this.headerContainer;
19657
19658        /* For Screen Reader and Keyboard accessibility */
19659        tabs.attr('tabindex', this.tabindex);
19660
19661        tabs.on('focus.tabview', function(e) {
19662            var focusedTab = $(this);
19663
19664            if(!focusedTab.hasClass('ui-state-disabled')) {
19665                focusedTab.addClass('ui-tabs-outline');
19666
19667                if($this.cfg.scrollable) {
19668                    if(focusedTab.position().left + focusedTab.width() > $this.navcrollerRight.position().left) {
19669                        $this.navcrollerRight.trigger('click.tabview');
19670                    }
19671                    else if(focusedTab.position().left < $this.navcrollerLeft.position().left) {
19672                        $this.navcrollerLeft.trigger('click.tabview');
19673                    }
19674                }
19675            }
19676        })
19677        .on('blur.tabview', function(){
19678            $(this).removeClass('ui-tabs-outline');
19679        })
19680        .on('keydown.tabview', function(e) {
19681            var keyCode = $.ui.keyCode,
19682            key = e.which,
19683            element = $(this);
19684
19685            if((key === keyCode.SPACE || key === keyCode.ENTER) && !element.hasClass('ui-state-disabled')) {
19686                $this.select(element.index());
19687                e.preventDefault();
19688            }
19689        });
19690
19691        //Scrolling
19692        if(this.cfg.scrollable) {
19693            this.navcrollerLeft.on('keydown.tabview', function(e) {
19694                var keyCode = $.ui.keyCode,
19695                key = e.which;
19696
19697                if(key === keyCode.SPACE || key === keyCode.ENTER) {
19698                    $this.scroll(100);
19699                    e.preventDefault();
19700                }
19701            });
19702
19703            this.navcrollerRight.on('keydown.tabview', function(e) {
19704                var keyCode = $.ui.keyCode,
19705                key = e.which;
19706
19707                if(key === keyCode.SPACE || key === keyCode.ENTER) {
19708                    $this.scroll(-100);
19709                    e.preventDefault();
19710                }
19711            });
19712        }
19713    },
19714
19715    initScrolling: function() {
19716        if(this.headerContainer.length) {
19717            var overflown = ((this.lastTab.position().left + this.lastTab.width()) 
19717- this.firstTab.position().left) > this.navscroller.innerWidth();
19718            if (overflown) {
19719                this.navscroller.removeClass('ui-tabs-navscroller-btn-hidden');
19720                this.navcrollerLeft.attr('tabindex', this.tabindex);
19721                this.navcrollerRight.attr('tabindex', this.tabindex);
19722                this.restoreScrollState();
19723            }
19724            else {
19725                this.navscroller.addClass('ui-tabs-navscroller-btn-hidden');
19726                this.navcrollerLeft.attr('tabindex', this.tabindex);
19727                this.navcrollerRight.attr('tabindex', this.tabindex);
19728            }
19729        }
19730    },
19731
19732    scroll: function(step) {
19733        if(this.navContainer.is(':animated')) {
19734            return;
19735        }
19736
19737        var oldMarginLeft = parseInt(this.navContainer.css('margin-left')),
19738        newMarginLeft = oldMarginLeft + step,
19739        viewportWidth = this.navscroller.innerWidth(),
19740        $this = this;
19741
19742        if(step < 0) {
19743            var lastTabBoundry = this.lastTab.position().left + parseInt(this.lastTab.innerWidth());
19744
19745            if(lastTabBoundry > viewportWidth)
19746                this.navContainer.animate({'margin-left': newMarginLeft + 'px'}, 'fast', 'easeInOutCirc', function() {
19747                    $this.saveScrollState(newMarginLeft);
19748
19749                    if((lastTabBoundry + step) < viewportWidth)
19750                        $this.disableScrollerButton($this.navcrollerRight);
19751                    if($this.navcrollerLeft.hasClass('ui-state-disabled'))
19752                        $this.enableScrollerButton($this.navcrollerLeft);
19753                });
19754        }
19755        else {
19756            if(newMarginLeft <= 0) {
19757                this.navContainer.animate({'margin-left': newMarginLeft + 'px'}, 'fast', 'easeInOutCirc', function() {
19758                    $this.saveScrollState(newMarginLeft);
19759
19760                    if(newMarginLeft === 0)
19761                        $this.disableScrollerButton($this.navcrollerLeft);
19762                    if($this.navcrollerRight.hasClass('ui-state-disabled'))
19763                        $this.enableScrollerButton($this.navcrollerRight);
19764                });
19765            }
19766        }
19767    },
19768
19769    disableScrollerButton: function(btn) {
19770        btn.addClass('ui-state-disabled').removeClass('ui-state-hover ui-state-active ui-state-focus').attr('tabindex', -1);
19771    },
19772
19773    enableScrollerButton: function(btn) {
19774        btn.removeClass('ui-state-disabled').attr('tabindex', this.tabindex);
19775    },
19776
19777    saveScrollState: function(value) {
19778        this.scrollStateHolder.val(value);
19779    },
19780
19781    restoreScrollState: function() {
19782        var value = parseInt(this.scrollStateHolder.val());
19783        if(value === 0) {
19784            this.disableScrollerButton(this.navcrollerLeft);
19785        }
19786
19787        this.navContainer.css('margin-left', this.scrollStateHolder.val() + 'px');
19788    },
19789
19790    /**
19791     * Selects an inactive tab given index
19792     */
19793    select: function(index, silent) {
19794        //Call user onTabChange callback
19795        if(this.cfg.onTabChange && !silent) {
19796            var result = this.cfg.onTabChange.call(this, index);
19797            if(result === false)
19798                return false;
19799        }
19800
19801        var newPanel = this.panelContainer.children().eq(index),
19802        shouldLoad = this.cfg.dynamic && !this.isLoaded(newPanel);
19803
19804        //update state
19805        this.stateHolder.val(newPanel.data('index'));
19806        this.cfg.selected = index;
19807
19808        if(shouldLoad) {
19809            this.loadDynamicTab(newPanel);
19810        }
19811        else {
19812            this.show(newPanel);
19813
19814            if(this.hasBehavior('tabChange') && !silent) {
19815                this.fireTabChangeEvent(newPanel);
19816            }
19817        }
19818
19819        return true;
19820    },
19821
19822    show: function(newPanel) {
19823        var headers = this.headerContainer,
19824        oldHeader = headers.filter('.ui-state-active'),
19825        oldActions = oldHeader.next('.ui-tabs-actions'),
19826        newHeader = headers.eq(newPanel.index()),
19827        newActions = newHeader.next('.ui-tabs-actions'),
19828        oldPanel = this.panelContainer.children('.ui-tabs-panel:visible'),
19829        _self = this;
19830
19831        //aria
19832        oldPanel.attr('aria-hidden', true);
19833        oldPanel.addClass('ui-helper-hidden');
19834        oldHeader.attr('aria-expanded', false);
19835        oldHeader.attr('aria-selected', false);
19836        if(oldActions.length != 0) {
19837            oldActions.attr('aria-hidden', true);
19838        }
19839
19840        newPanel.attr('aria-hidden', false);
19841        newPanel.removeClass('ui-helper-hidden');
19842        newHeader.attr('aria-expanded', true);
19843        newHeader.attr('aria-selected', true);
19844        if(newActions.length != 0) {
19845            newActions.attr('aria-hidden', false);
19846        }
19847
19848        if(this.cfg.effect) {
19849            oldPanel.hide(this.cfg.effect, null, this.cfg.effectDuration, function() {
19850                oldHeader.removeClass('ui-tabs-selected ui-state-active');
19851                if(oldActions.length != 0) {
19852                    oldActions.hide(_self.cfg.effect, null, _self.cfg.effectDuration);
19853                }
19854
19855                newHeader.addClass('ui-tabs-selected ui-state-active');
19856                newPanel.show(_self.cfg.effect, null, _self.cfg.effectDuration, function() {
19857                    _self.postTabShow(newPanel);
19858                });
19859                if(newActions.length != 0) {
19860                    newActions.show(_self.cfg.effect, null, _self.cfg.effectDuration);
19861                }
19862            });
19863        }
19864        else {
19865            oldHeader.removeClass('ui-tabs-selected ui-state-active');
19866            oldPanel.hide();
19867            if(oldActions.length != 0) {
19868                oldActions.hide();
19869            }
19870
19871            newHeader.addClass('ui-tabs-selected ui-state-active');
19872            newPanel.show();
19873            if(newActions.length != 0) {
19874                newActions.show();
19875            }
19876
19877            this.postTabShow(newPanel);
19878        }
19879    },
19880
19881    /**
19882     * Loads tab contents with ajax
19883     */
19884    loadDynamicTab: function(newPanel) {
19885        var $this = this,
19886        options = {
19887            source: this.id,
19888            process: this.id,
19889            update: this.id,
19890            params: [
19891                {name: this.id + '_contentLoad', value: true},
19892                {name: this.id + '_newTab', value: newPanel.attr('id')},
19893                {name: this.id + '_tabindex', value: newPanel.data('index')}
19894            ],
19895            onsuccess: function(responseXML, status, xhr) {
19896                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
19897                        widget: $this,
19898                        handle: function(content) {
19899                            newPanel.html(content);
19900
19901                            if(this.cfg.cache) {
19902                                this.markAsLoaded(newPanel);
19903                            }
19904                        }
19905                    });
19906
19907                return true;
19908            },
19909            oncomplete: function() {
19910                $this.show(newPanel);
19911            }
19912        };
19913
19914        if(this.hasBehavior('tabChange')) {
19915            this.callBehavior('tabChange', options);
19916        }
19917        else {
19918            PrimeFaces.ajax.Request.handle(options);
19919        }
19920    },
19921
19922    /**
19923     * Removes a tab with given index
19924     */
19925    remove: function(index) {
19926        // remove old header and content
19927        var header = this.headerContainer.eq(index),
19928        panel = this.panelContainer.children().eq(index);
19929
19930        header.remove();
19931        panel.remove();
19932
19933        // refresh "chached" selectors
19934        this.headerContainer = this.navContainer.children('li.ui-tabs-header');
19935        this.panelContainer = this.jq.children('.ui-tabs-panels');
19936
19937        // select next tab
19938        var length = this.getLength();
19939        if(length > 0) {
19940            if(index < this.cfg.selected) {
19941                this.cfg.selected--;
19942            }
19943            else if(index === this.cfg.selected) {
19944                var newIndex = (this.cfg.selected === (length)) ? (this.cfg.selected - 1): this.cfg.selected,
19945                newPanelHeader = this.headerContainer.eq(newIndex);
19946
19947                if(newPanelHeader.hasClass('ui-state-disabled')) {
19948                    var newHeader = this.headerContainer.filter(':not(.ui-state-disabled):first');
19949                    if(newHeader.length) {
19950                        this.select(newHeader.index(), true);
19951                    }
19952                }
19953                else {
19954                    this.select(newIndex, true);
19955                }
19956            }
19957        }
19958        else {
19959            this.cfg.selected = -1;
19960        }
19961
19962        this.fireTabCloseEvent(panel.attr('id'), index);
19963    },
19964
19965    getLength: function() {
19966        return this.navContainer.children().length;
19967    },
19968
19969    getActiveIndex: function() {
19970        return this.cfg.selected;
19971    },
19972
19973    fireTabChangeEvent: function(panel) {
19974        var ext = {
19975            params: [
19976                {name: this.id + '_newTab', value: panel.attr('id')},
19977                {name: this.id + '_tabindex', value: panel.data('index')}
19978            ]
19979        };
19980
19981        this.callBehavior('tabChange', ext);
19982    },
19983
19984    fireTabCloseEvent: function(id, index) {
19985        if(this.hasBehavior('tabClose')) {
19986            var ext = {
19987                params: [
19988                    {name: this.id + '_closeTab', value: id},
19989                    {name: this.id + '_tabindex', value: index}
19990                ]
19991            };
19992
19993            this.callBehavior('tabClose', ext);
19994        }
19995    },
19996
19997    markAsLoaded: function(panel) {
19998        panel.data('loaded', true);
19999    },
20000
20001    isLoaded: function(panel) {
20002        return panel.data('loaded') === true;
20003    },
20004
20005    disable: function(index) {
20006        this.headerContainer.eq(index).addClass('ui-state-disabled');
20007    },
20008
20009    enable: function(index) {
20010        this.headerContainer.eq(index).removeClass('ui-state-disabled');
20011    },
20012
20013    postTabShow: function(newPanel) {
20014        //execute user defined callback
20015        if(this.cfg.onTabShow) {
20016            this.cfg.onTabShow.call(this, newPanel.index());
20017        }
20018
20019        PrimeFaces.invokeDeferredRenders(this.id);
20020    }
20021
20022});
20023
20024/**
20025 * PrimeFaces TagCloud Widget
20026 */
20027PrimeFaces.widget.TagCloud = PrimeFaces.widget.BaseWidget.extend({
20028
20029    init: function(cfg) {
20030        this._super(cfg);
20031        var _self = this;
20032
20033        this.jq.find('a').mouseover(function() {
20034            $(this).addClass('ui-state-hover');
20035        })
20036        .mouseout(function() {
20037            $(this).removeClass('ui-state-hover');
20038        })
20039        .click(function(e) {
20040            var link = $(this);
20041
20042            if(link.attr('href') === '#') {
20043                _self.fireSelectEvent(link);
20044                e.preventDefault();
20045            }
20046        });
20047    },
20048
20049    fireSelectEvent: function(link) {
20050        if(this.hasBehavior('select')) {
20051            var ext = {
20052                params: [
20053                    {name: this.id + '_itemIndex', value: link.parent().index()}
20054                ]
20055            };
20056
20057            this.callBehavior('select', ext);
20058        }
20059    }
20060
20061});
20062/**
20063 * PrimeFaces Tooltip Widget
20064 */
20065PrimeFaces.widget.Tooltip = PrimeFaces.widget.BaseWidget.extend({
20066
20067    init: function(cfg) {
20068        this.cfg = cfg;
20069        this.id = this.cfg.id;
20070        this.cfg.showEvent = this.cfg.showEvent ? this.cfg.showEvent + '.tooltip' : 'mouseover.tooltip';
20071        this.cfg.hideEvent = this.cfg.hideEvent ? this.cfg.hideEvent + '.tooltip' : 'mouseout.tooltip';
20072        this.cfg.showEffect = this.cfg.showEffect ? this.cfg.showEffect : 'fade';
20073        this.cfg.hideEffect = this.cfg.hideEffect ? this.cfg.hideEffect : 'fade';
20074        this.cfg.showDelay = this.cfg.showDelay||150;
20075        this.cfg.hideDelay = this.cfg.hideDelay||0;
20076        this.cfg.hideEffectDuration = this.cfg.target ? 250 : 1;
20077        this.cfg.position = this.cfg.position||'right';
20078        this.cfg.escape = (this.cfg.escape === undefined) ? true : this.cfg.escape;
20079
20080        if(this.cfg.target)
20081            this.bindTarget();
20082        else
20083            this.bindGlobal();
20084
20085        this.removeScriptElement(this.id);
20086    },
20087
20088    //@override
20089    refresh: function(cfg) {
20090        if(cfg.target) {
20091            var targetTooltip = $(document.body).children(PrimeFaces.escapeClientId(cfg.id));
20092            if(targetTooltip.length)
20093                targetTooltip.remove();
20094        }
20095        else {
20096            $(document.body).children('.ui-tooltip-global').remove();
20097        }
20098
20099        this._super(cfg);
20100    },
20101
20102    bindGlobal: function() {
20103        this.jq = $('<div class="ui-tooltip ui-tooltip-global ui-widget ui-tooltip-' + this.cfg.position + '" role="tooltip"></div>')
20104            .appendTo('body');
20105        this.jq.append('<div class="ui-tooltip-arrow"></div><div class="ui-tooltip-text ui-shadow ui-corner-all"></div>');
20106
20107        this.jq.addClass(this.cfg.styleClass);
20108        
20109        this.cfg.globalSelector = this.cfg.globalSelector||'a,:input,:button';
20110        var $this = this;
20111
20112        $(document).off(this.cfg.showEvent + ' ' + this.cfg.hideEvent, this.cfg.globalSelector)
20113                    .on(this.cfg.showEvent, this.cfg.globalSelector, function(e) {
20114                        var element = $(this);
20115                        if(element.prop('disabled')) {
20116                            return;
20117                        }
20118
20119                        if($this.cfg.trackMouse) {
20120                            $this.mouseEvent = e;
20121                        }
20122
20123                        var title = element.attr('title');
20124                        if(title) {
20125                            element.data('tooltip', title).removeAttr('title');
20126                        }
20127
20128                        var arrow = $this.jq.children('.ui-tooltip-arrow');
20129
20130                        if(element.hasClass('ui-state-error')) {
20131                            $this.jq.children('.ui-tooltip-text').addClass('ui-state-error');
20132                            arrow.addClass('ui-state-error');
20133                        }
20134                        else {
20135                            arrow.removeClass('ui-state-error');
20136                        }
20137
20138                        var text = element.data('tooltip');
20139                        if(text) {
20140                            if($this.cfg.escape)
20141                                $this.jq.children('.ui-tooltip-text').text(text);
20142                            else
20143                                $this.jq.children('.ui-tooltip-text').html(text);
20144
20145                            $this.globalTitle = text;
20146                            $this.target = element;
20147                            $this.show();
20148                        }
20149                    })
20150                    .on(this.cfg.hideEvent + '.tooltip', this.cfg.globalSelector, function() {
20151                        if($this.globalTitle) {
20152                            $this.hide();
20153                            $this.globalTitle = null;
20154                            $this.target = null;
20155                            $this.jq.children('.ui-tooltip-text').removeClass('ui-state-error');
20156                        }
20157                    });
20158
20159        PrimeFaces.utils.registerResizeHandler(this, 'resize.tooltip' + '_align', $this.jq, function() {
20160            $this.align();
20161        });
20162
20163    },
20164
20165    bindTarget: function() {
20166        this.id = this.cfg.id;
20167        this.jqId = PrimeFaces.escapeClientId(this.id);
20168        this.jq = $(this.jqId);
20169        this.target = PrimeFaces.expressions.SearchExpressionFacade.resolve
20169ComponentsAsSelector(this.cfg.target);
20170
20171        var describedBy = this.target.attr("aria-describedby");
20172        if (!describedBy || 0 === describedBy.length) {
20173            describedBy = this.id;
20174        } else {
20175            describedBy += " " + this.id;
20176        }
20177        this.target.attr("aria-describedby", describedBy);
20178
20179        var $this = this;
20180        if(this.cfg.delegate) {
20181            var targetSelector = "*[id='" + this.target.attr('id') + "']";
20182
20183            $(document).off(this.cfg.showEvent + ' ' + this.cfg.hideEvent, targetSelector)
20184                        .on(this.cfg.showEvent, targetSelector, function(e) {
20185                            if($this.cfg.trackMouse) {
20186                                $this.mouseEvent = e;
20187                            }
20188
20189                            if($.trim($this.jq.children('.ui-tooltip-text').html()) !== '') {
20190                                $this.show();
20191                            }
20192                        })
20193                        .on(this.cfg.hideEvent + '.tooltip', function() {
20194                            $this.hide();
20195                        });
20196        }
20197        else {
20198            this.target.off(this.cfg.showEvent + ' ' + this.cfg.hideEvent)
20199                        .on(this.cfg.showEvent, function(e) {
20200                            if($this.cfg.trackMouse) {
20201                                $this.mouseEvent = e;
20202                            }
20203
20204                            if($.trim($this.jq.children('.ui-tooltip-text').html()) !== '') {
20205                                $this.show();
20206                            }
20207                        })
20208                        .on(this.cfg.hideEvent + '.tooltip', function() {
20209                            $this.hide();
20210                        });
20211        }
20212
20213        this.jq.appendTo(document.body);
20214
20215        if($.trim(this.jq.children('.ui-tooltip-text').html()) === '') {
20216            var text = this.target.attr('title');
20217            if(this.cfg.escape)
20218                this.jq.children('.ui-tooltip-text').text(text);
20219            else
20220                this.jq.children('.ui-tooltip-text').html(text);
20221        }
20222
20223        this.target.removeAttr('title');
20224
20225
20226        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.jq, function() {
20227            $this.align();
20228        });
20229    },
20230
20231    alignUsing: function(position,feedback) {
20232        this.jq.removeClass('ui-tooltip-left ui-tooltip-right ui-tooltip-top ui-tooltip-bottom');
20233        switch (this.cfg.position) {
20234        case "right":
20235        case "left":
20236            this.jq.addClass('ui-tooltip-'+
20237                    (feedback['horizontal']=='left'?'right':'left'));
20238            break;
20239        case "top":
20240        case "bottom":
20241            this.jq.addClass('ui-tooltip-'+
20242                    (feedback['vertical']=='top'?'bottom':'top'));
20243            break;
20244        }
20245        this.jq.css({
20246            left: position['left'],
20247            top: position['top']
20248        });
20249    },
20250
20251    align: function() {
20252        var $this = this;
20253         this.jq.css({
20254            left:'',
20255            top:'',
20256            'z-index': ++PrimeFaces.zindex
20257        });
20258
20259        if(this.cfg.trackMouse && this.mouseEvent) {
20260            this.jq.position({
20261                my: 'left top+15',
20262                at: 'right bottom',
20263                of: this.mouseEvent,
20264                collision: 'flipfit',
20265                using: function(p,f) {
20266                    $this.alignUsing.call($this,p,f);
20267                }
20268            });
20269
20270            this.mouseEvent = null;
20271        }
20272        else {
20273            var _my, _at;
20274
20275            switch(this.cfg.position) {
20276                case 'right':
20277                    _my = 'left center';
20278                    _at = 'right center';
20279                break;
20280
20281                case 'left':
20282                    _my = 'right center';
20283                    _at = 'left center';
20284                break;
20285
20286                case 'top':
20287                    _my = 'center bottom';
20288                    _at = 'center top';
20289                break;
20290
20291                case 'bottom':
20292                    _my = 'center top';
20293                    _at = 'center bottom';
20294                break;
20295            }
20296
20297            this.jq.position({
20298                my: _my,
20299                at: _at,
20300                of: this.getTarget(),
20301                collision: 'flipfit',
20302                using: function(p,f) {
20303                    $this.alignUsing.call($this,p,f);
20304                }
20305            });
20306        }
20307    },
20308
20309    show: function() {
20310        if(this.getTarget()) {
20311            var $this = this;
20312            this.clearTimeout();
20313
20314            this.timeout = setTimeout(function() {
20315                $this._show();
20316            }, this.cfg.showDelay);
20317        }
20318    },
20319
20320    _show: function() {
20321        var $this = this;
20322
20323        if(this.cfg.beforeShow) {
20324            var retVal = this.cfg.beforeShow.call(this);
20325            if(retVal === false) {
20326                return;
20327            }
20328        }
20329
20330        this.jq.css({'display':'block', 'opacity':0, 'pointer-events': 'none'});
20331    
20332        this.align();
20333
20334        this.jq.css({'display':'none', 'opacity':'', 'pointer-events': ''});
20335        
20336        if(this.cfg.trackMouse) {
20337            this.followMouse();
20338        }
20339        this.jq.show(this.cfg.showEffect, {}, 250, function() {
20340            if($this.cfg.onShow) {
20341                $this.cfg.onShow.call();
20342            }
20343        });
20344    },
20345
20346    hide: function() {
20347        var $this = this;
20348        this.clearTimeout();
20349
20350        if(this.cfg.hideDelay) {
20351            this.timeout = setTimeout(function() {
20352                $this._hide();
20353            }, this.cfg.hideDelay);
20354        }
20355        else {
20356            this._hide();
20357        }
20358    },
20359
20360    _hide: function() {
20361        var $this = this;
20362
20363        if(this.isVisible()) {
20364            this.jq.hide(this.cfg.hideEffect, {}, this.cfg.hideEffectDuration, function() {
20365                $(this).css('z-index', '');
20366                if($this.cfg.trackMouse) {
20367                    $this.unfollowMouse();
20368                }
20369
20370                if($this.cfg.onHide) {
20371                    $this.cfg.onHide.call();
20372                }
20373            });
20374        }
20375    },
20376
20377    clearTimeout: function() {
20378        if(this.timeout) {
20379            clearTimeout(this.timeout);
20380        }
20381    },
20382
20383    followMouse: function() {
20384        var $this = this;
20385
20386        this.getTarget().on('mousemove.tooltip-track', function(e) {
20387            $this.jq.position({
20388                my: 'left top+15',
20389                at: 'right bottom',
20390                of: e,
20391                collision: 'flipfit'
20392            });
20393        });
20394    },
20395
20396    unfollowMouse: function() {
20397        var target = this.getTarget();
20398        if(target) {
20399            target.off('mousemove.tooltip-track');
20400        }
20401    },
20402
20403    isVisible: function() {
20404        return this.jq.is(':visible');
20405    },
20406
20407    getTarget: function() {
20408        if(this.cfg.delegate)
20409            return PrimeFaces.expressions.SearchExpressionFacade.resolve
20409ComponentsAsSelector(this.cfg.target);
20410        else
20411            return this.target;
20412    }
20413
20414});
20415
20416/**
20417 * PrimeFaces Base Tree Widget
20418 */
20419PrimeFaces.widget.BaseTree = PrimeFaces.widget.BaseWidget.extend({
20420
20421    init: function(cfg) {
20422        this._super(cfg);
20423        this.cfg.highlight = (this.cfg.highlight === false) ? false : true;
20424        this.focusedNode = null;
20425
20426        if(!this.cfg.disabled) {
20427            if(this.cfg.selectionMode) {
20428                this.initSelection();
20429            }
20430
20431            this.bindEvents();
20432
20433            this.jq.data('widget', cfg.widgetVar);
20434        }
20435    },
20436
20437    initSelection: function() {
20438        this.selectionHolder = $(this.jqId + '_selection');
20439        var selectionsValue = this.selectionHolder.val();
20440        this.selections = selectionsValue === '' ? [] : selectionsValue.split(',');
20441
20442        if(this.cursorNode) {
20443            this.cursorNode = this.jq.find('.ui-treenode[data-rowkey="' + $.escapeSelector(this.cursorNode.data('rowkey')) + '"]');
20444        }
20445
20446        if(this.isCheckboxSelection() && this.cfg.propagateUp) {
20447            this.preselectCheckbox();
20448        }
20449    },
20450
20451    bindContextMenu : function(menuWidget, targetWidget, targetId, cfg) {
20452        var nodeContentSelector = targetId + ' .ui-tree-selectable',
20453        nodeEvent = cfg.nodeType ? cfg.event + '.treenode.' + cfg.nodeType : cfg.event + '.treenode',
20454        containerEvent = cfg.event + '.tree';
20455
20456        $(document).off(nodeEvent, nodeContentSelector).on(nodeEvent, nodeContentSelector, null, function(e) {
20457            var nodeContent = $(this);
20458
20459            if($(e.target).is(':not(.ui-tree-toggler)') && (cfg.nodeType === undefined || nodeContent.parent().data('nodetype') === cfg.nodeType)) {
20460                var isContextMenuDelayed = targetWidget.nodeRightClick(e, nodeContent, function(){
20461                    menuWidget.show(e);
20462                });
20463
20464                if(isContextMenuDelayed) {
20465                    e.preventDefault();
20466                    e.stopPropagation(); 
20467                }
20468            }
20469        });
20470
20471        $(document).off(containerEvent, this.jqTargetId).on(containerEvent, this.jqTargetId, null, function(e) {
20472            if(targetWidget.isEmpty()) {
20473                menuWidget.show(e);
20474            }
20475        });
20476    },
20477
20478    expandNode: function(node) {
20479        var $this = this;
20480
20481        if(this.cfg.dynamic) {
20482            if(this.cfg.cache && $this.getNodeChildrenContainer(node).children().length > 0) {
20483                this.showNodeChildren(node);
20484
20485                return;
20486            }
20487
20488            if(node.data('processing')) {
20489                PrimeFaces.debug('Node is already being expanded, ignoring expand event.');
20490                return;
20491            }
20492
20493            node.data('processing', true);
20494
20495            var options = {
20496                source: this.id,
20497                process: this.id,
20498                update: this.id,
20499                formId: this.cfg.formId,
20500                params: [
20501                    {name: this.id + '_expandNode', value: $this.getRowKey(node)}
20502                ],
20503                onsuccess: function(responseXML, status, xhr) {
20504                    PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
20505                            widget: $this,
20506                            handle: function(content) {
20507                                var nodeChildrenContainer = this.getNodeChildrenContainer(node);
20508                                nodeChildrenContainer.append(content);
20509
20510                                this.showNodeChildren(node);
20511
20512                                if(this.cfg.draggable) {
20513                                    this.makeDraggable(nodeChildrenContainer.find('span.ui-treenode-content'));
20514                                }
20515
20516                                if(this.cfg.droppable) {
20517                                    this.makeDropPoints(nodeChildrenContainer.find('li.ui-tree-droppoint'));
20518                                    this.makeDropNodes(nodeChildrenContainer.find('span.ui-treenode-droppable'));
20519                                }
20520                            }
20521                        });
20522
20523                    return true;
20524                },
20525                oncomplete: function() {
20526                    node.removeData('processing');
20527                }
20528            };
20529
20530            if(this.hasBehavior('expand')) {
20531                this.callBehavior('expand', options);
20532            }
20533            else {
20534                PrimeFaces.ajax.Request.handle(options);
20535            }
20536        }
20537        else {
20538            this.showNodeChildren(node);
20539            this.fireExpandEvent(node);
20540        }
20541    },
20542
20543    fireExpandEvent: function(node) {
20544        if(this.hasBehavior('expand')) {
20545            var ext = {
20546                params: [
20547                    {name: this.id + '_expandNode', value: this.getRowKey(node)}
20548                ]
20549            };
20550
20551            this.callBehavior('expand', ext);
20552        }
20553    },
20554
20555    fireCollapseEvent: function(node) {
20556        if(this.hasBehavior('collapse')) {
20557            var ext = {
20558                params: [
20559                    {name: this.id + '_collapseNode', value: this.getRowKey(node)}
20560                ]
20561            };
20562
20563            this.callBehavior('collapse', ext);
20564        }
20565    },
20566
20567    getNodeChildrenContainer: function(node) {
20568        throw "Unsupported Operation";
20569    },
20570
20571    showNodeChildren: function(node) {
20572        throw "Unsupported Operation";
20573    },
20574
20575    writeSelections: function() {
20576        this.selectionHolder.val(this.selections.join(','));
20577    },
20578
20579    fireNodeSelectEvent: function(node) {
20580        if(this.isCheckboxSelection() && this.cfg.dynamic) {
20581            var $this = this,
20582            options = {
20583                source: this.id,
20584                process: this.id
20585            };
20586
20587            options.params = [
20588                {name: this.id + '_instantSelection', value: this.getRowKey(node)}
20589            ];
20590
20591            options.oncomplete = function(xhr, status, args, data) {
20592                if(args.descendantRowKeys && args.descendantRowKeys !== '') {
20593                    var rowKeys = args.descendantRowKeys.split(',');
20594                    for(var i = 0; i < rowKeys.length; i++) {
20595                        $this.addToSelection(rowKeys[i]);
20596                    }
20597                    $this.writeSelections();
20598                }
20599            };
20600
20601            if(this.hasBehavior('select')) {
20602                this.callBehavior('select', options);
20603            }
20604            else {
20605                PrimeFaces.ajax.Request.handle(options);
20606            }
20607        }
20608        else {
20609            if(this.hasBehavior('select')) {
20610                var ext = {
20611                    params: [
20612                        {name: this.id + '_instantSelection', value: this.getRowKey(node)}
20613                    ]
20614                };
20615
20616                this.callBehavior('select', ext);
20617            }
20618        }
20619    },
20620
20621    fireNodeUnselectEvent: function(node) {
20622        if(this.hasBehavior('unselect')) {
20623            var ext = {
20624                params: [
20625                    {name: this.id + '_instantUnselection', value: this.getRowKey(node)}
20626                ]
20627            };
20628
20629            this.callBehavior('unselect', ext);
20630        }
20631    },
20632
20633    fireContextMenuEvent: function(node, fnShowMenu) {
20634        if(this.hasBehavior('contextMenu')) {
20635            var ext = {
20636                params: [
20637                    {name: this.id + '_contextMenuNode', value: this.getRowKey(node)}
20638                ],
20639                oncomplete: function() {
20640                    fnShowMenu();
20641                }
20642            };
20643
20644            this.callBehavior('contextMenu', ext);
20645        } else {
20646            fnShowMenu();
20647        }
20648    },
20649
20650    getRowKey: function(node) {
20651        return node.attr('data-rowkey');
20652    },
20653
20654    isNodeSelected: function(node) {
20655        return $.inArray(this.getRowKey(node), this.selections) != -1;
20656    },
20657
20658    isSingleSelection: function() {
20659        return this.cfg.selectionMode == 'single';
20660    },
20661
20662    isMultipleSelection: function() {
20663        return this.cfg.selectionMode == 'multiple';
20664    },
20665
20666    isCheckboxSelection: function() {
20667        return this.cfg.selectionMode == 'checkbox';
20668    },
20669
20670    addToSelection: function(rowKey) {
20671        if(!PrimeFaces.inArray(this.selections, rowKey)) {
20672            this.selections.push(rowKey);
20673        }
20674    },
20675
20676    removeFromSelection: function(rowKey) {
20677        this.selections = $.grep(this.selections, function(r) {
20678            return r !== rowKey;
20679        });
20680    },
20681
20682    removeDescendantsFromSelection: function(rowKey) {
20683        var newSelections = [];
20684        for(var i = 0; i < this.selections.length; i++) {
20685            if(this.selections[i].indexOf(rowKey + '_') !== 0)
20686                newSelections.push(this.selections[i]);
20687        }
20688        this.selections = newSelections;
20689    },
20690
20691    nodeClick: function(event, nodeContent) {
20692        if($(event.target).is(':not(.ui-tree-toggler)')) {
20693            var node = nodeContent.parent(),
20694            selectable = nodeContent.hasClass('ui-tree-selectable');
20695
20696            if(this.cfg.onNodeClick) {
20697                var retVal = this.cfg.onNodeClick.call(this, node, event);
20698                if (retVal === false) {
20699                    return;
20700                }
20701            }
20702
20703            if(selectable && this.cfg.selectionMode) {
20704                var selected = this.isNodeSelected(node),
20705                metaKey = event.metaKey||event.ctrlKey,
20706                shiftKey = event.shiftKey;
20707
20708                if(this.isCheckboxSelection()) {
20709                    this.toggleCheckboxNode(node);
20710                }
20711                else {
20712                    if(selected && (metaKey)) {
20713                        this.unselectNode(node);
20714                    }
20715                    else {
20716                        if(this.isSingleSelection()||(this.isMultipleSelection() && !metaKey)) {
20717                            this.unselectAllNodes();
20718                        }
20719
20720                        if(this.isMultipleSelection() && shiftKey && this.cursorNode && (this.cursorNode.parent().is(node.parent()))) {
20721                            var parentList = node.parent(),
20722                            treenodes = parentList.children('li.ui-treenode'),
20723                            currentNodeIndex = treenodes.index(node),
20724                            cursorNodeIndex = treenodes.index(this.cursorNode),
20725                            startIndex = (currentNodeIndex > cursorNodeIndex) ? cursorNodeIndex : currentNodeIndex,
20726                            endIndex = (currentNodeIndex > cursorNodeIndex) ? (currentNodeIndex + 1) : (cursorNodeIndex + 1);
20727
20728                            for(var i = startIndex; i < endIndex; i++) {
20729                                var treenode = treenodes.eq(i);
20730                                if(treenode.is(':visible')) {
20731                                    if(i === (endIndex - 1))
20732                                        this.selectNode(treenode);
20733                                    else
20734                                        this.selectNode(treenode, true);
20735                                }
20736                            }
20737                        }
20738                        else {
20739                            this.selectNode(node);
20740                            this.cursorNode = node;
20741                        }
20742                    }
20743                }
20744
20745                if($(event.target).is(':not(:input:enabled)')) {
20746                    PrimeFaces.clearSelection();
20747                    this.focusNode(node);
20748                }
20749            }
20750        }
20751    },
20752
20753    nodeRightClick: function(event, nodeContent, fnShowMenu) {
20754        PrimeFaces.clearSelection();
20755
20756        if($(event.target).is(':not(.ui-tree-toggler)')) {
20757            var node = nodeContent.parent(),
20758            selectable = nodeContent.hasClass('ui-tree-selectable');
20759
20760            if(selectable && this.cfg.selectionMode) {
20761                var selected = this.isNodeSelected(node);
20762                if(!selected) {
20763                    if(this.isCheckboxSelection()) {
20764                        this.toggleCheckboxNode(node);
20765                    }
20766                    else {
20767                        this.unselectAllNodes();
20768                        this.selectNode(node, true);
20769                        this.cursorNode = node;
20770                    }
20771                }
20772
20773                this.fireContextMenuEvent(node, fnShowMenu);
20774                return true;
20775            }
20776        }
20777        return false;
20778    },
20779
20780    bindEvents: function() {
20781        throw "Unsupported Operation";
20782    },
20783
20784    selectNode: function(node, silent) {
20785        throw "Unsupported Operation";
20786    },
20787
20788    unselectNode: function(node, silent) {
20789        throw "Unsupported Operation";
20790    },
20791
20792    unselectAllNodes: function() {
20793        throw "Unsupported Operation";
20794    },
20795
20796    preselectCheckbox: function() {
20797        throw "Unsupported Operation";
20798    },
20799
20800    toggleCheckboxNode: function(node) {
20801        throw "Unsupported Operation";
20802    },
20803
20804    isEmpty: function() {
20805        throw "Unsupported Operation";
20806    },
20807
20808    toggleCheckboxState: function(checkbox, checked) {
20809        if(checked)
20810            this.uncheck(checkbox);
20811        else
20812            this.check(checkbox);
20813    },
20814
20815    partialCheck: function(checkbox) {
20816        var box = checkbox.children('.ui-chkbox-box'),
20817        icon = box.children('.ui-chkbox-icon'),
20818        treeNode = checkbox.closest('.ui-treenode'),
20819        rowKey = this.getRowKey(treeNode);
20820
20821        treeNode.find('> .ui-treenode-content > .ui-treenode-label').removeClass('ui-state-highlight');
20822        icon.removeClass('ui-icon-blank ui-icon-check').addClass('ui-icon-minus');
20823        treeNode.removeClass('ui-treenode-selected ui-treenode-unselected').addClass('ui-treenode-hasselected').attr('aria-checked', false).attr('aria-selected', false);
20824
20825        this.removeFromSelection(rowKey);
20826    },
20827
20828    check: function(checkbox) {
20829        var box = checkbox.children('.ui-chkbox-box'),
20830        icon = box.children('.ui-chkbox-icon'),
20831        treeNode = checkbox.closest('.ui-treenode'),
20832        rowKey = this.getRowKey(treeNode);
20833
20834        box.removeClass('ui-state-hover');
20835        icon.removeClass('ui-icon-blank ui-icon-minus').addClass('ui-icon-check');
20836        treeNode.removeClass('ui-treenode-hasselected ui-treenode-unselected').addClass('ui-treenode-selected').attr('aria-checked', true).attr('aria-selected', true);
20837
20838        this.addToSelection(rowKey);
20839    },
20840
20841    uncheck: function(checkbox) {
20842        var box = checkbox.children('.ui-chkbox-box'),
20843        icon = box.children('.ui-chkbox-icon'),
20844        treeNode = checkbox.closest('.ui-treenode'),
20845        rowKey = this.getRowKey(treeNode);
20846
20847        box.removeClass('ui-state-hover');
20848        icon.removeClass('ui-icon-minus ui-icon-check').addClass('ui-icon-blank');
20849        treeNode.removeClass('ui-treenode-hasselected ui-treenode-selected').addClass('ui-treenode-unselected').attr('aria-checked', false).attr('aria-selected', false);
20850
20851        this.removeFromSelection(rowKey);
20852    },
20853
20854    isExpanded: function(node) {
20855        return this.getNodeChildrenContainer(node).is(':visible');
20856    },
20857
20858    focusNode: function() {
20859        throw "Unsupported Operation";
20860    }
20861
20862});
20863
20864/**
20865 * PrimeFaces Vertical Tree Widget
20866 */
20867PrimeFaces.widget.VerticalTree = PrimeFaces.widget.BaseTree.extend({
20868
20869    init: function(cfg) {
20870        this._super(cfg);
20871
20872        this.container = this.jq.children('.ui-tree-container');
20873        this.cfg.rtl = this.jq.hasClass('ui-tree-rtl');
20874        this.cfg.collapsedIcon = this.cfg.rtl ? 'ui-icon-triangle-1-w' : 'ui-icon-triangle-1-e';
20875        this.scrollStateHolder = $(this.jqId + '_scrollState');
20876
20877        if(!this.cfg.disabled) {
20878            if(this.cfg.draggable) {
20879                this.initDraggable();
20880            }
20881
20882            if(this.cfg.droppable) {
20883                this.initDroppable();
20884            }
20885        }
20886
20887        this.restoreScrollState();
20888    },
20889
20890    bindEvents: function() {
20891        var $this = this,
20892        togglerSelector = '.ui-tree-toggler',
20893        nodeLabelSelector = '.ui-tree-selectable .ui-treenode-label',
20894        nodeContentSelector = '.ui-treenode-content';
20895
20896        this.jq.off('click.tree-toggle', togglerSelector)
20897                    .on('click.tree-toggle', togglerSelector, null, function(e) {
20898                        var toggleIcon = $(this),
20899                        node = toggleIcon.closest('li');
20900
20901                        if(toggleIcon.hasClass($this.cfg.collapsedIcon))
20902                            $this.expandNode(node);
20903                        else
20904                            $this.collapseNode(node);
20905                    });
20906
20907        if(this.cfg.highlight && this.cfg.selectionMode) {
20908            this.jq.off('mouseout.tree mouseover.tree', nodeLabelSelector)
20909                        .on('mouseout.tree', nodeLabelSelector, null, function() {
20910                            var label = $(this);
20911
20912                            label.removeClass('ui-state-hover');
20913
20914                            if($this.isCheckboxSelection()) {
20915                                label.siblings('div.ui-chkbox').children('div.ui-chkbox-box').removeClass('ui-state-hover');
20916                            }
20917                        })
20918                        .on('mouseover.tree', nodeLabelSelector, null, function() {
20919                            var label = $(this);
20920
20921                            $(this).addClass('ui-state-hover');
20922
20923                            if($this.isCheckboxSelection()) {
20924                                label.siblings('div.ui-chkbox').children('div.ui-chkbox-box').addClass('ui-state-hover');
20925                            }
20926                        });
20927        }
20928
20929        if(this.isCheckboxSelection()) {
20930            var checkboxSelector = '.ui-chkbox-box:not(.ui-state-disabled)';
20931
20932            this.jq.off('mouseout.tree-checkbox mouseover.tree-checkbox click.tree-checkbox', checkboxSelector)
20933                        .on('mouseout.tree-checkbox', checkboxSelector, null, function() {
20934                            $(this).removeClass('ui-state-hover').parent().siblings('span.ui-treenode-label').removeClass('ui-state-hover');
20935                        })
20936                        .on('mouseover.tree-checkbox', checkboxSelector, null, function() {
20937                            $(this).addClass('ui-state-hover').parent().siblings('span.ui-treenode-label').addClass('ui-state-hover');
20938                        });
20939        }
20940
20941        this.jq.off('click.tree-content', nodeContentSelector)
20942                        .on('click.tree-content', nodeContentSelector, null, function(e) {
20943                            $this.nodeClick(e, $(this));
20944                        });
20945
20946        if(this.cfg.filter) {
20947            this.filterInput = this.jq.find('.ui-tree-filter');
20948            PrimeFaces.skinInput(this.filterInput);
20949
20950            this.filterInput.on('keydown.tree-filter', PrimeFaces.utils.blockEnterKey)
20951            .on('keyup.tree-filter', function(e) {
20952                if (PrimeFaces.utils.ignoreFilterKey(e)) {
20953                    return;
20954                }
20955                
20956                if($this.filterTimeout) {
20957                    clearTimeout($this.filterTimeout);
20958                }
20959
20960                $this.filterTimeout = setTimeout(function() {
20961                    $this.filter();
20962                    $this.filterTimeout = null;
20963                }, 300);
20964            });
20965        }
20966
20967        this.jq.on('scroll.tree', function(e) {
20968            $this.saveScrollState();
20969        });
20970
20971        this.bindKeyEvents();
20972    },
20973
20974    bindKeyEvents: function() {
20975        var $this = this,
20976        pressTab = false;
20977
20978        this.jq.on('mousedown.tree', function(e) {
20979            if($(e.target).is(':not(:input:enabled)')) {
20980                e.preventDefault();
20981            }
20982        })
20983        .on('focus.tree', function() {
20984            if(!$this.focusedNode && !pressTab) {
20985                $this.focusNode($this.getFirstNode());
20986            }
20987        });
20988
20989        this.jq.off('keydown.tree blur.tree', '.ui-treenode-label').on('keydown.tree', '.ui-treenode-label', null, function(e) {
20990            if(!$this.focusedNode) {
20991                return;
20992            }
20993
20994            var searchRowkey = "",
20995            keyCode = $.ui.keyCode;
20996
20997            switch(e.which) {
20998                case keyCode.LEFT:
20999                    var rowkey = $this.focusedNode.data('rowkey').toString(),
21000                    keyLength = rowkey.length;
21001
21002                    if($this.isExpanded($this.focusedNode)) {
21003                        $this.collapseNode($this.focusedNode);
21004                    }
21005                    else {
21006                        var nodeToFocus = null;
21007                        for(var i = 1; i < parseInt(keyLength / 2) + 1; i++){
21008                            searchRowkey = rowkey.substring(0, keyLength - 2 * i);
21009                            nodeToFocus = $this.container.find("li:visible[data-rowkey = '" + searchRowkey + "']");
21010                            if(nodeToFocus.length) {
21011                                $this.focusNode(nodeToFocus);
21012                                break;
21013                            }
21014                        }
21015                    }
21016
21017                    e.preventDefault();
21018                break;
21019
21020                case keyCode.RIGHT:
21021                    if(!$this.focusedNode.hasClass('ui-treenode-leaf')) {
21022                        var rowkey = $this.focusedNode.data('rowkey').toString(),
21023                        keyLength = rowkey.length;
21024
21025                        if(!$this.isExpanded($this.focusedNode)) {
21026                            $this.expandNode($this.focusedNode);
21027                        }
21028
21029                        if(!$this.isExpanded($this.focusedNode) && !$this.cfg.dynamic) {
21030                            searchRowkey = rowkey + '_0';
21031                            var nodeToFocus = $this.container.find("li:visible[data-rowkey = '" + searchRowkey + "']");
21032
21033                            if(nodeToFocus.length) {
21034                                $this.focusNode(nodeToFocus);
21035                            }
21036                        }
21037                    }
21038
21039                    e.preventDefault();
21040                break;
21041
21042                case keyCode.UP:
21043                    var nodeToFocus = null,
21044                    prevNode = $this.focusedNode.prev();
21045
21046                    if(prevNode.length) {
21047                        nodeToFocus = prevNode.find('li.ui-treenode:visible:last');
21048                        if(!nodeToFocus.length) {
21049                            nodeToFocus = prevNode;
21050                        }
21051                    }
21052                    else {
21053                        nodeToFocus = $this.focusedNode.closest('ul').parent('li');
21054                    }
21055
21056                    if(nodeToFocus.length) {
21057                        $this.focusNode(nodeToFocus);
21058                    }
21059
21060                    e.preventDefault();
21061                break;
21062
21063                case keyCode.DOWN:
21064                    var nodeToFocus = null,
21065                    firstVisibleChildNode = $this.focusedNode.find("> ul > li:visible:first");
21066
21067                    if(firstVisibleChildNode.length) {
21068                        nodeToFocus = firstVisibleChildNode;
21069                    }
21070                    else if($this.focusedNode.next().length) {
21071                        nodeToFocus = $this.focusedNode.next();
21072                    }
21073                    else {
21074                        var rowkey = $this.focusedNode.data('rowkey').toString();
21075
21076                        if(rowkey.length !== 1) {
21077                            nodeToFocus = $this.searchDown($this.focusedNode);
21078                        }
21079                    }
21080
21081                    if(nodeToFocus && nodeToFocus.length) {
21082                        $this.focusNode(nodeToFocus);
21083                    }
21084
21085                    e.preventDefault();
21086                break;
21087
21088                case keyCode.ENTER:
21089                case keyCode.SPACE:
21090                    if($this.cfg.selectionMode) {
21091                        var selectable = $this.focusedNode.children('.ui-treenode-content').hasClass('ui-tree-selectable');
21092
21093                        if($this.cfg.onNodeClick) {
21094                            var retVal = $this.cfg.onNodeClick.call($this, $this.focusedNode, e);
21095                            if (retVal === false) {
21096                                return;
21097                            }
21098                        }
21099
21100                        if(selectable) {
21101                            var selected = $this.isNodeSelected($this.focusedNode);
21102
21103                            if($this.isCheckboxSelection()) {
21104                                $this.toggleCheckboxNode($this.focusedNode);
21105                            }
21106                            else {
21107                                if(selected) {
21108                                    $this.unselectNode($this.focusedNode);
21109                                }
21110                                else {
21111                                    if($this.isSingleSelection()) {
21112                                        $this.unselectAllNodes();
21113                                    }
21114
21115                                    $this.selectNode($this.focusedNode);
21116                                    $this.cursorNode = $this.focusedNode;
21117                                }
21118                            }
21119                        }
21120                    }
21121
21122                    e.preventDefault();
21123                break;
21124
21125                case keyCode.TAB:
21126                    pressTab = true;
21127                    $this.jq.focus();
21128                    setTimeout(function() {
21129                        pressTab = false;
21130                    }, 2);
21131
21132                break;
21133            }
21134        })
21135        .on('blur.tree', '.ui-treenode-label', null, function(e) {
21136            if($this.focusedNode) {
21137                $this.getNodeLabel($this.focusedNode).removeClass('ui-treenode-outline');
21138                $this.focusedNode = null;
21139            }
21140        });
21141
21142        /* For copy/paste operation on drag and drop */
21143        $(document.body).on('keydown.tree', function(e) {
21144            $this.shiftKey = e.shiftKey;
21145        })
21146        .on('keyup.tree', function() {
21147            $this.shiftKey = false;
21148        });
21149    },
21150
21151    searchDown: function(node) {
21152        var nextOfParent = node.closest('ul').parent('li').next(),
21153        nodeToFocus = null;
21154
21155        if(nextOfParent.length) {
21156            nodeToFocus = nextOfParent;
21157        }
21158        else if(node.hasClass('ui-treenode-leaf') && node.closest('ul').parent('li').length == 0){
21159            nodeToFocus = node;
21160        }
21161        else {
21162            var rowkey = node.data('rowkey').toString();
21163
21164            if(rowkey.length !== 1) {
21165                nodeToFocus = this.searchDown(node.closest('ul').parent('li'));
21166            }
21167        }
21168
21169        return nodeToFocus;
21170    },
21171
21172    collapseNode: function(node) {
21173        var _self = this,
21174        nodeContent = node.find('> .ui-treenode-content'),
21175        toggleIcon = nodeContent.find('> .ui-tree-toggler'),
21176        nodeType = node.data('nodetype'),
21177        nodeIcon = toggleIcon.nextAll('span.ui-treenode-icon'),
21178        iconState = this.cfg.iconStates[nodeType],
21179        childrenContainer = node.children('.ui-treenode-children');
21180
21181        //aria
21182        nodeContent.find('> .ui-treenode-label').attr('aria-expanded', false);
21183
21184        toggleIcon.removeClass('ui-icon-triangle-1-s').addClass(_self.cfg.collapsedIcon);
21185
21186        if(iconState) {
21187            nodeIcon.removeClass(iconState.expandedIcon).addClass(iconState.collapsedIcon);
21188        }
21189
21190        if(this.cfg.animate) {
21191            childrenContainer.slideUp('fast', function() {
21192                _self.postCollapse(node, childrenContainer);
21193            });
21194        }
21195        else {
21196            childrenContainer.hide();
21197            this.postCollapse(node, childrenContainer);
21198        }
21199    },
21200
21201    postCollapse: function(node, childrenContainer) {
21202        if(this.cfg.dynamic && !this.cfg.cache) {
21203            childrenContainer.empty();
21204        }
21205
21206        if(!this.cfg.cache) {
21207            this.fireCollapseEvent(node);
21208        }
21209    },
21210
21211    //@Override
21212    getNodeChildrenContainer: function(node) {
21213        return node.children('.ui-treenode-children');
21214    },
21215
21216    //@Override
21217    showNodeChildren: function(node) {
21218        var nodeContent = node.find('> .ui-treenode-content'),
21219        toggleIcon = nodeContent.find('> .ui-tree-toggler'),
21220        nodeType = node.data('nodetype'),
21221        nodeIcon = toggleIcon.nextAll('span.ui-treenode-icon'),
21222        iconState = this.cfg.iconStates[nodeType];
21223
21224        //aria
21225        nodeContent.find('> .ui-treenode-label').attr('aria-expanded', true);
21226
21227        toggleIcon.removeClass(this.cfg.collapsedIcon).addClass('ui-icon-triangle-1-s');
21228
21229        if(iconState) {
21230            nodeIcon.removeClass(iconState.collapsedIcon).addClass(iconState.expandedIcon);
21231        }
21232
21233        if(this.cfg.animate) {
21234            node.children('.ui-treenode-children').slideDown('fast');
21235        }
21236        else {
21237            node.children('.ui-treenode-children').show();
21238        }
21239    },
21240
21241    unselectAllNodes: function() {
21242        this.selections = [];
21243        this.jq.find('.ui-treenode-label.ui-state-highlight').each(function() {
21244            $(this).removeClass('ui-state-highlight').closest('.ui-treenode').attr('aria-selected', false);
21245        });
21246    },
21247
21248    selectNode: function(node, silent) {
21249        node.attr('aria-selected', true)
21250            .find('> .ui-treenode-content > .ui-treenode-label').removeClass('ui-state-hover').addClass('ui-state-highlight');
21251
21252        this.addToSelection(this.getRowKey(node));
21253        this.writeSelections();
21254
21255        if(!silent)
21256            this.fireNodeSelectEvent(node);
21257    },
21258
21259    unselectNode: function(node, silent) {
21260        var rowKey = this.getRowKey(node);
21261
21262        node.attr('aria-selected', false).
21263            find('> .ui-treenode-content > .ui-treenode-label').removeClass('ui-state-highlight ui-state-hover');
21264
21265        this.removeFromSelection(rowKey);
21266        this.writeSelections();
21267
21268        if(!silent)
21269            this.fireNodeUnselectEvent(node);
21270    },
21271
21272    toggleCheckboxNode: function(node) {
21273        var $this = this,
21274        checkbox = node.find('> .ui-treenode-content > .ui-chkbox'),
21275        checked = checkbox.find('> .ui-chkbox-box > .ui-chkbox-icon').hasClass('ui-icon-check');
21276
21277        this.toggleCheckboxState(checkbox, checked);
21278
21279        if(this.cfg.propagateDown) {
21280            node.children('.ui-treenode-children').find('.ui-chkbox').each(function() {
21281                $this.toggleCheckboxState($(this), checked);
21282            });
21283
21284            if(this.cfg.dynamic) {
21285                this.removeDescendantsFromSelection(node.data('rowkey'));
21286            }
21287        }
21288
21289        if(this.cfg.propagateUp) {
21290            node.parents('li.ui-treenode-parent').each(function() {
21291                var parentNode = $(this),
21292                parentsCheckbox = parentNode.find('> .ui-treenode-content > .ui-chkbox'),
21293                children = parentNode.find('> .ui-treenode-children > .ui-treenode');
21294
21295                if(checked) {
21296                    if(children.filter('.ui-treenode-unselected').length === children.length)
21297                        $this.uncheck(parentsCheckbox);
21298                    else
21299                        $this.partialCheck(parentsCheckbox);
21300                }
21301                else {
21302                    if(children.filter('.ui-treenode-selected').length === children.length)
21303                        $this.check(parentsCheckbox);
21304                    else
21305                        $this.partialCheck(parentsCheckbox);
21306                }
21307            });
21308        }
21309
21310        this.writeSelections();
21311
21312        if(checked)
21313            this.fireNodeUnselectEvent(node);
21314        else
21315            this.fireNodeSelectEvent(node);
21316    },
21317
21318    preselectCheckbox: function() {
21319        this.jq.find('.ui-chkbox-icon').not('.ui-icon-check').each(function() {
21320            var icon = $(this),
21321            node = icon.closest('li');
21322
21323            if(node.children('.ui-treenode-children').find('.ui-chkbox-icon.ui-icon-check').length > 0) {
21324                node.addClass('ui-treenode-hasselected');
21325                icon.removeClass('ui-icon-blank').addClass('ui-icon-minus');
21326            }
21327        });
21328    },
21329
21330    check: function(checkbox) {
21331        this._super(checkbox);
21332        checkbox.siblings('span.ui-treenode-label').addClass('ui-state-highlight').removeClass('ui-state-hover');
21333    },
21334
21335    uncheck: function(checkbox) {
21336        this._super(checkbox);
21337        checkbox.siblings('span.ui-treenode-label').removeClass('ui-state-highlight');
21338    },
21339
21340    initDraggable: function() {
21341        this.makeDraggable(this.jq.find('span.ui-treenode-content'));
21342    },
21343
21344    initDroppable: function() {
21345        this.makeDropPoints(this.jq.find('li.ui-tree-droppoint'));
21346        this.makeDropNodes(this.jq.find('span.ui-treenode-droppable'));
21347        this.initDropScrollers();
21348    },
21349
21350    makeDraggable: function(elements) {
21351        var $this = this,
21352        dragdropScope = this.cfg.dragdropScope||this.id;
21353
21354        elements.draggable({
21355            start: function(event, ui) {
21356                if(ui.helper) {
21357                    var element = $(event.target),
21358                    source = PF($(element.data('dragsourceid')).data('widget')),
21359                    height = 20;
21360
21361                    if(source.cfg.multipleDrag && element.find('.ui-treenode-label').hasClass('ui-state-highlight')) {
21362                        source.draggedSourceKeys = $this.findSelectedParentKeys(source.selections.slice());
21363                        height = 20 * (source.draggedSourceKeys.length || 1);
21364                    }
21365
21366                    $(ui.helper).height(height);
21367                }
21368            },
21369            helper: function() {
21370                var el = $('<div class="ui-tree-draghelper ui-state-highlight"></div>');
21371                el.width($this.jq.width());
21372                return el;
21373            },
21374            appendTo: document.body,
21375            zIndex: ++PrimeFaces.zindex,
21376            revert: true,
21377            scope: dragdropScope,
21378            containment: 'document'
21379        })
21380        .data({
21381            'dragsourceid': this.jqId,
21382            'dragmode': this.cfg.dragMode
21383        });
21384    },
21385
21386    makeDropPoints: function(elements) {
21387        var $this = this,
21388        dragdropScope = this.cfg.dragdropScope||this.id;
21389
21390        elements.droppable({
21391            hoverClass: 'ui-state-hover',
21392            accept: 'span.ui-treenode-content',
21393            tolerance: 'pointer',
21394            scope: dragdropScope,
21395            drop: function(event, ui) {
21396                var dragSource = PF($(ui.draggable.data('dragsourceid')).data('widget')),
21397                dropSource = $this,
21398                dropPoint = $(this),
21399                dropNode = dropPoint.closest('li.ui-treenode-parent'),
21400                dropNodeKey = $this.getRowKey(dropNode),
21401                transfer = (dragSource.id !== dropSource.id),
21402                draggedSourceKeys = dragSource.draggedSourceKeys,
21403                isDroppedNodeCopy = ($this.cfg.dropCopyNode && $this.shiftKey),
21404                draggedNodes,
21405                dragNodeKey;
21406
21407                if(draggedSourceKeys) {
21408                    draggedNodes = dragSource.findNodes(draggedSourceKeys);
21409                }
21410                else {
21411                    draggedNodes = [ui.draggable];
21412                }
21413
21414                if($this.cfg.controlled) {
21415                    $this.droppedNodeParams = [];
21416                }
21417
21418                $this.invalidSourceKeys = [];
21419
21420                for(var i = (draggedNodes.length - 1); i >= 0; i--) {
21421                    var draggedNode = $(draggedNodes[i]),
21422                    dragMode = ui.draggable.data('dragmode'),
21423                    dragNode = draggedNode.is('li.ui-treenode') ? draggedNode : draggedNode.closest('li.ui-treenode'),
21424                    dragNode = (isDroppedNodeCopy) ? dragNode.clone() : dragNode,
21425                    targetDragNode = $this.findTargetDragNode(dragNode, dragMode);
21426
21427                    dragNodeKey = $this.getRowKey(targetDragNode);
21428
21429                    if(!transfer && dropNodeKey && dropNodeKey.indexOf(dragNodeKey) === 0) {
21430                        return;
21431                    }
21432
21433                    if($this.cfg.controlled) {
21434                        $this.droppedNodeParams.push({
21435                            'ui': ui,
21436                            'dragSource': dragSource,
21437                            'dragNode': dragNode,
21438                            'targetDragNode': targetDragNode,
21439                            'dropPoint': dropPoint,
21440                            'dropNode': dropNode,
21441                            'transfer': transfer
21442                        });
21443                    }
21444                    else {
21445                        $this.onDropPoint(ui, dragSource, dragNode, targetDragNode, dropPoint, dropNode, transfer);
21446                    }
21447                }
21448
21449                if (!draggedSourceKeys) {
21450                    draggedSourceKeys = [dragNodeKey];
21451                }
21452                draggedSourceKeys = draggedSourceKeys.filter(function(key) {
21453                    return $.inArray(key, $this.invalidSourceKeys) === -1;
21454                });
21455
21456                if (draggedSourceKeys && draggedSourceKeys.length) {
21457                    draggedSourceKeys = draggedSourceKeys.reverse().join(',');
21458                    $this.fireDragDropEvent({
21459                        'dragNodeKey': draggedSourceKeys,
21460                        'dropNodeKey': dropNodeKey,
21461                        'dragSource': dragSource.id,
21462                        'dndIndex': dropPoint.prevAll('li.ui-treenode').length,
21463                        'transfer': transfer,
21464                        'isDroppedNodeCopy': isDroppedNodeCopy
21465                    });
21466                }
21467
21468                dragSource.draggedSourceKeys = null;
21469                $this.invalidSourceKeys = null;
21470
21471                if(isDroppedNodeCopy) {
21472                    $this.initDraggable();
21473                }
21474            }
21475        });
21476    },
21477
21478    onDropPoint: function(ui, dragSource, dragNode, targetDragNode, dropPoint, dropNode, transfer) {
21479        var dragNodeDropPoint = targetDragNode.next('li.ui-tree-droppoint'),
21480        oldParentNode = targetDragNode.parent().closest('li.ui-treenode-parent');
21481
21482        ui.helper.remove();
21483        dropPoint.removeClass('ui-state-hover');
21484
21485        var validDrop = this.validateDropPoint(dragNode, dropPoint);
21486        if(!validDrop) {
21487            if (this.invalidSourceKeys) {
21488                var dragNodeKey = this.getRowKey(targetDragNode);
21489                this.invalidSourceKeys.push(dragNodeKey);
21490            }
21491            return;
21492        }
21493
21494        targetDragNode.hide().insertAfter(dropPoint);
21495
21496        if(transfer) {
21497            if(dragSource.cfg.selectionMode) {
21498                dragSource.unselectSubtree(targetDragNode);
21499            }
21500
21501            dragNodeDropPoint.remove();
21502            this.updateDragDropBindings(targetDragNode);
21503        }
21504        else {
21505            dragNodeDropPoint.insertAfter(targetDragNode);
21506        }
21507
21508        if(oldParentNode.length && (oldParentNode.find('> ul.ui-treenode-children > li.ui-treenode').length === 0)) {
21509            this.makeLeaf(oldParentNode);
21510        }
21511
21512        targetDragNode.fadeIn();
21513
21514        if(this.isCheckboxSelection()) {
21515            this.syncDNDCheckboxes(dragSource, oldParentNode, dropNode);
21516        }
21517
21518        this.syncDragDrop();
21519        if(transfer) {
21520            dragSource.syncDragDrop();
21521        }
21522    },
21523
21524    makeDropNodes: function(elements) {
21525        var $this = this,
21526        dragdropScope = this.cfg.dragdropScope||this.id;
21527
21528        elements.droppable({
21529            accept: '.ui-treenode-content',
21530            tolerance: 'pointer',
21531            scope: dragdropScope,
21532            over: function(event, ui) {
21533                $(this).children('.ui-treenode-label').addClass('ui-state-hover');
21534            },
21535            out: function(event, ui) {
21536                $(this).children('.ui-treenode-label').removeClass('ui-state-hover');
21537            },
21538            drop: function(event, ui) {
21539                var dragSource = PF($(ui.draggable.data('dragsourceid')).data('widget')),
21540                dropSource = $this,
21541                droppable = $(this),
21542                dropNode = droppable.closest('li.ui-treenode'),
21543                dropNodeKey = $this.getRowKey(dropNode),
21544                transfer = (dragSource.id !== dropSource.id),
21545                draggedSourceKeys = dragSource.draggedSourceKeys,
21546                isDroppedNodeCopy = ($this.cfg.dropCopyNode && $this.shiftKey),
21547                draggedNodes,
21548                dragNodeKey,
21549                dndIndex;
21550
21551                if(draggedSourceKeys) {
21552                    draggedNodes = dragSource.findNodes(draggedSourceKeys);
21553                }
21554                else {
21555                    draggedNodes = [ui.draggable];
21556                }
21557
21558                if($this.cfg.controlled) {
21559                    $this.droppedNodeParams = [];
21560                }
21561
21562                $this.invalidSourceKeys = [];
21563
21564                for(var i = 0; i < draggedNodes.length; i++) {
21565                    var draggedNode = $(draggedNodes[i]),
21566                    dragMode = ui.draggable.data('dragmode'),
21567                    dragNode = draggedNode.is('li.ui-treenode') ? draggedNode : draggedNode.closest('li.ui-treenode'),
21568                    dragNode = (isDroppedNodeCopy) ? dragNode.clone() : dragNode,
21569                    targetDragNode = $this.findTargetDragNode(dragNode, dragMode);
21570
21571                    if(i === 0) {
21572                        dndIndex = dropNode.find('>.ui-treenode-children>li.ui-treenode').length;
21573                    }
21574
21575                    dragNodeKey = $this.getRowKey(targetDragNode);
21576
21577                    if(!transfer && dropNodeKey && dropNodeKey.indexOf(dragNodeKey) === 0) {
21578                        return;
21579                    }
21580
21581                    if($this.cfg.controlled) {
21582                        $this.droppedNodeParams.push({
21583                            'ui': ui,
21584                            'dragSource': dragSource,
21585                            'dragNode': dragNode,
21586                            'targetDragNode': targetDragNode,
21587                            'droppable': droppable,
21588                            'dropNode': dropNode,
21589                            'transfer': transfer
21590                        });
21591                    }
21592                    else {
21593                        $this.onDropNode(ui, dragSource, dragNode, targetDragNode, droppable, dropNode, transfer);
21594                    }
21595                }
21596
21597                if (!draggedSourceKeys) {
21598                    draggedSourceKeys = [dragNodeKey];
21599                }
21600                draggedSourceKeys = draggedSourceKeys.filter(function(key) {
21601                    return $.inArray(key, $this.invalidSourceKeys) === -1;
21602                });
21603
21604                if (draggedSourceKeys && draggedSourceKeys.length) {
21605                    draggedSourceKeys = draggedSourceKeys.reverse().join(',');
21606                    $this.fireDragDropEvent({
21607                        'dragNodeKey': draggedSourceKeys,
21608                        'dropNodeKey': dropNodeKey,
21609                        'dragSource': dragSource.id,
21610                        'dndIndex': dndIndex,
21611                        'transfer': transfer,
21612                        'isDroppedNodeCopy': isDroppedNodeCopy
21613                    });
21614                }
21615
21616                dragSource.draggedSourceKeys = null;
21617                $this.invalidSourceKeys = null;
21618
21619                if(isDroppedNodeCopy) {
21620                    $this.initDraggable();
21621                }
21622            }
21623        });
21624    },
21625
21626    onDropNode: function(ui, dragSource, dragNode, targetDragNode, droppable, dropNode, transfer) {
21627        var dragNodeDropPoint = targetDragNode.next('li.ui-tree-droppoint'),
21628        oldParentNode = targetDragNode.parent().closest('li.ui-treenode-parent'),
21629        childrenContainer = dropNode.children('.ui-treenode-children');
21630
21631        ui.helper.remove();
21632        droppable.children('.ui-treenode-label').removeClass('ui-state-hover');
21633
21634        var validDrop = this.validateDropNode(dragNode, dropNode, oldParentNode);
21635        if(!validDrop) {
21636            if (this.invalidSourceKeys) {
21637                var dragNodeKey = this.getRowKey(targetDragNode);
21638                this.invalidSourceKeys.push(dragNodeKey);
21639            }
21640            return;
21641        }
21642
21643        if(childrenContainer.children('li.ui-treenode').length === 0) {
21644            this.makeParent(dropNode);
21645        }
21646
21647        targetDragNode.hide();
21648        childrenContainer.append(targetDragNode);
21649
21650        if(oldParentNode.length && (oldParentNode.find('> ul.ui-treenode-children > li.ui-treenode').length === 0)) {
21651            this.makeLeaf(oldParentNode);
21652        }
21653
21654        if(transfer) {
21655            if(dragSource.cfg.selectionMode) {
21656                dragSource.unselectSubtree(targetDragNode);
21657            }
21658
21659            dragNodeDropPoint.remove();
21660            this.updateDragDropBindings(targetDragNode);
21661        }
21662        else {
21663            childrenContainer.append(dragNodeDropPoint);
21664        }
21665
21666        targetDragNode.fadeIn();
21667
21668        if(this.isCheckboxSelection()) {
21669            this.syncDNDCheckboxes(dragSource, oldParentNode, dropNode);
21670        }
21671
21672        this.syncDragDrop();
21673        if(transfer) {
21674            dragSource.syncDragDrop();
21675        }
21676    },
21677
21678    findSelectedParentKeys: function(arr) {
21679        for(var i = 0; i < arr.length; i++) {
21680            var key = arr[i];
21681            for(var j = 0; j < arr.length && key !== -1; j++) {
21682                var tempKey = arr[j];
21683                if(tempKey !== -1 && key.length > tempKey.length && key.indexOf(tempKey) === 0) {
21684                    arr[i] = -1;
21685                }
21686            }
21687        }
21688
21689        return arr.filter(function(item) {
21690            return item !== -1;
21691        });
21692    },
21693
21694    initDropScrollers: function() {
21695        var $this = this,
21696        dragdropScope = this.cfg.dragdropScope||this.id;
21697
21698        this.jq.prepend('<div class="ui-tree-scroller ui-tree-scrollertop"></div>').append('<div class="ui-tree-scroller ui-tree-scrollerbottom"></div>');
21699
21700        this.jq.children('div.ui-tree-scroller').droppable({
21701            accept: '.ui-treenode-content',
21702            tolerance: 'pointer',
21703            scope: dragdropScope,
21704            over: function() {
21705                var step = $(this).hasClass('ui-tree-scrollertop') ? -10 : 10;
21706
21707                $this.scrollInterval = setInterval(function() {
21708                    $this.scroll(step);
21709                }, 100);
21710            },
21711            out: function() {
21712                clearInterval($this.scrollInterval);
21713            }
21714        });
21715    },
21716
21717    scroll: function(step) {
21718        this.container.scrollTop(this.container.scrollTop() + step);
21719    },
21720
21721    updateDragDropBindings: function(node) {
21722        //self droppoint
21723        node.after('<li class="ui-tree-droppoint ui-droppable"></li>');
21724        this.makeDropPoints(node.next('li.ui-tree-droppoint'));
21725
21726        //descendant droppoints
21727        var subtreeDropPoints = node.find('li.ui-tree-droppoint');
21728        if(subtreeDropPoints.hasClass('ui-droppable') && !this.shiftKey && !this.cfg.dropCopyNode) {
21729            subtreeDropPoints.droppable('destroy');
21730        }
21731        this.makeDropPoints(subtreeDropPoints);
21732
21733        //descendant drop node contents
21734        var subtreeDropNodeContents = node.find('span.ui-treenode-content');
21735        if(subtreeDropNodeContents.hasClass('ui-droppable') && !this.shiftKey && !this.cfg.dropCopyNode) {
21736            subtreeDropNodeContents.droppable('destroy');
21737        }
21738        this.makeDropNodes(subtreeDropNodeContents);
21739
21740        if(this.cfg.draggable) {
21741            subtreeDropNodeContents.data({
21742                'dragsourceid': this.jqId,
21743                'dragmode': this.cfg.dragMode
21744            });
21745        }
21746    },
21747
21748    findTargetDragNode: function(dragNode, dragMode) {
21749        var targetDragNode = null;
21750
21751        if(dragMode === 'self') {
21752            targetDragNode = dragNode;
21753        } else if(dragMode === 'parent') {
21754            targetDragNode = dragNode.parent().closest('li.ui-treenode');
21755        } else if(dragMode === 'ancestor') {
21756            targetDragNode = dragNode.parent().parents('li.ui-treenode:last');
21757        }
21758
21759        if(targetDragNode.length === 0) {
21760            targetDragNode = dragNode;
21761        }
21762
21763        return targetDragNode;
21764    },
21765
21766    findNodes: function(rowkeys) {
21767        var nodes = [];
21768        for(var i = 0; i < rowkeys.length; i++) {
21769            nodes.push($(this.jqId + '\\:' + rowkeys[i]));
21770        }
21771
21772        return nodes;
21773    },
21774
21775    updateRowKeys: function() {
21776        var children = this.jq.find('> ul.ui-tree-container > li.ui-treenode');
21777        this.updateChildrenRowKeys(children, null);
21778    },
21779
21780    updateChildrenRowKeys: function(children, rowkey) {
21781        var $this = this;
21782
21783        children.each(function(i) {
21784            var childNode = $(this),
21785            oldRowKey = childNode.attr('data-rowkey'),
21786            newRowKey = (rowkey === null) ? i.toString() : rowkey + '_' + i;
21787
21788            childNode.attr({
21789                'id': $this.id + ':' + newRowKey,
21790                'data-rowkey' : newRowKey
21791            });
21792
21793            if(childNode.hasClass('ui-treenode-parent')) {
21794                $this.updateChildrenRowKeys(childNode.find('> ul.ui-treenode-children > li.ui-treenode'), newRowKey);
21795            }
21796        });
21797    },
21798
21799    validateDropPoint: function(dragNode, dropPoint) {
21800        //dropped before or after
21801        if(dragNode.next().get(0) === dropPoint.get(0)||dragNode.prev().get(0) === dropPoint.get(0)) {
21802            return false;
21803        }
21804
21805        //descendant of dropnode
21806        if(dragNode.has(dropPoint.get(0)).length) {
21807            return false;
21808        }
21809
21810        //drop restriction
21811        if(this.cfg.dropRestrict) {
21812            if(this.cfg.dropRestrict === 'sibling' && dragNode.parent().get(0) !== dropPoint.parent().get(0)) {
21813                return false;
21814            }
21815        }
21816
21817        return true;
21818    },
21819
21820    validateDropNode: function(dragNode, dropNode, oldParentNode) {
21821        //dropped on parent
21822        if(oldParentNode.get(0) === dropNode.get(0))
21823            return false;
21824
21825        //descendant of dropnode
21826        if(dragNode.has(dropNode.get(0)).length) {
21827            return false;
21828        }
21829
21830        //drop restriction
21831        if(this.cfg.dropRestrict) {
21832            if(this.cfg.dropRestrict === 'sibling') {
21833                return false;
21834            }
21835        }
21836
21837        return true;
21838    },
21839
21840    makeLeaf: function(node) {
21841        node.removeClass('ui-treenode-parent').addClass('ui-treenode-leaf');
21842        node.find('> .ui-treenode-content > .ui-tree-toggler').addClass('ui-treenode-leaf-icon').removeClass('ui-tree-toggler ui-icon ui-icon-triangle-1-s');
21843        node.children('.ui-treenode-children').hide().children().remove();
21844    },
21845
21846    makeParent: function(node) {
21847        node.removeClass('ui-treenode-leaf').addClass('ui-treenode-parent');
21848        node.find('> span.ui-treenode-content > span.ui-treenode-leaf-icon').removeClass('ui-treenode-leaf-icon').addClass('ui-tree-toggler ui-icon ui-icon-triangle-1-e');
21849        node.children('.ui-treenode-children').append('<li class="ui-tree-droppoint ui-droppable"></li>');
21850
21851        this.makeDropPoints(node.find('> ul.ui-treenode-children > li.ui-tree-droppoint'));
21852    },
21853
21854    syncDragDrop: function() {
21855        var $this = this;
21856
21857        if(this.cfg.selectionMode) {
21858            var selectedNodes = this.findNodes(this.selections);
21859
21860            this.updateRowKeys();
21861            this.selections = [];
21862            $.each(selectedNodes, function(i, item) {
21863                $this.selections.push(item.attr('data-rowkey'));
21864            });
21865            this.writeSelections();
21866        }
21867        else {
21868            this.updateRowKeys();
21869        }
21870    },
21871
21872    syncDNDCheckboxes: function(dragSource, oldParentNode, newParentNode) {
21873        if(oldParentNode.length) {
21874            dragSource.propagateDNDCheckbox(oldParentNode);
21875        }
21876
21877        if(newParentNode.length) {
21878            this.propagateDNDCheckbox(newParentNode);
21879        }
21880    },
21881
21882    unselectSubtree: function(node) {
21883        var $this = this;
21884
21885        if(this.isCheckboxSelection()) {
21886            var checkbox = node.find('> .ui-treenode-content > .ui-chkbox');
21887
21888            this.toggleCheckboxState(checkbox, true);
21889
21890            node.children('.ui-treenode-children').find('.ui-chkbox').each(function() {
21891                $this.toggleCheckboxState($(this), true);
21892            });
21893        }
21894        else {
21895            node.find('.ui-treenode-label.ui-state-highlight').each(function() {
21896                $(this).removeClass('ui-state-highlight').closest('li.ui-treenode').attr('aria-selected', false);
21897            });
21898        }
21899    },
21900
21901    propagateDNDCheckbox: function(node) {
21902        var checkbox = node.find('> .ui-treenode-content > .ui-chkbox'),
21903        children = node.find('> .ui-treenode-children > .ui-treenode');
21904
21905        if(children.length) {
21906            if(children.filter('.ui-treenode-unselected').length === children.length)
21907                this.uncheck(checkbox);
21908            else if(children.filter('.ui-treenode-selected').length === children.length)
21909                this.check(checkbox);
21910            else
21911                this.partialCheck(checkbox);
21912        }
21913
21914        var parent = node.parent().closest('.ui-treenode-parent');
21915        if(parent.length) {
21916            this.propagateDNDCheckbox(parent);
21917        }
21918    },
21919
21920    fireDragDropEvent: function(event) {
21921        var $this = this,
21922        options = {
21923            source: this.id,
21924            process: event.transfer ? this.id + ' ' + event.dragSource : this.id
21925        };
21926
21927        options.params = [
21928            {name: this.id + '_dragdrop', value: true},
21929            {name: this.id + '_dragNode', value: event.dragNodeKey},
21930            {name: this.id + '_dragSource', value: event.dragSource},
21931            {name: this.id + '_dropNode', value: event.dropNodeKey},
21932            {name: this.id + '_dndIndex', value: event.dndIndex},
21933            {name: this.id + '_isDroppedNodeCopy', value: event.isDroppedNodeCopy}
21934        ];
21935
21936        if(this.cfg.controlled) {
21937            options.oncomplete = function(xhr, status, args, data) {
21938                if(args.access) {
21939                    for(var i = 0; i < $this.droppedNodeParams.length; i++) {
21940                        var params = $this.droppedNodeParams[i];
21941                        if(params.dropPoint)
21942                            $this.onDropPoint(params.ui, params.dragSource, params.dragNode, params.targetDragNode, params.dropPoint, params.dropNode, params.transfer);
21943                        else
21944                            $this.onDropNode(params.ui, params.dragSource, params.dragNode, params.targetDragNode, params.droppable, params.dropNode, params.transfer);
21945                    }
21946                }
21947            };
21948        }
21949
21950        if(this.hasBehavior('dragdrop')) {
21951            this.callBehavior('dragdrop', options);
21952        }
21953        else {
21954            PrimeFaces.ajax.Request.handle(options);
21955        }
21956    },
21957
21958    isEmpty: function() {
21959        return (this.container.children().length === 0);
21960    },
21961
21962    getFirstNode: function() {
21963        return this.jq.find('> ul.ui-tree-container > li:first-child');
21964    },
21965
21966    getNodeLabel: function(node) {
21967        return node.find('> span.ui-treenode-content > span.ui-treenode-label');
21968    },
21969
21970    focusNode: function(node) {
21971        if(this.focusedNode) {
21972            this.getNodeLabel(this.focusedNode).removeClass('ui-treenode-outline');
21973        }
21974
21975        this.getNodeLabel(node).addClass('ui-treenode-outline').focus();
21976        this.focusedNode = node;
21977    },
21978
21979    /**
21980     * Ajax filter
21981     */
21982    filter: function() {
21983        var $this = this,
21984        options = {
21985            source: this.id,
21986            update: this.id,
21987            process: this.id,
21988            global: false,
21989            formId: this.cfg.formId,
21990            params: [{name: this.id + '_filtering', value: true},
21991                     {name: this.id + '_encodeFeature', value: true}],
21992            onsuccess: function(responseXML, status, xhr) {
21993                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
21994                    widget: $this,
21995                    handle: function(content) {
21996                        $this.container.html(content);
21997                    }
21998                });
21999
22000                return true;
22001            },
22002            oncomplete: function() {
22003                if ($this.cfg.filterMode === 'contains') {
22004                    var notLeafNodes = $this.container.find('li.ui-treenode:not(.ui-treenode-leaf):visible');
22005                    for(var i = 0; i < notLeafNodes.length; i++) {
22006                        var node = notLeafNodes.eq(i),
22007                        hasChildNodes = node.children('.ui-treenode-children:empty').length;
22008
22009                        if(hasChildNodes) {
22010                            node.removeClass('ui-treenode-parent').addClass('ui-treenode-leaf')
22011                                .find('> .ui-treenode-content > .ui-tree-toggler').removeClass('ui-tree-toggler ui-icon ui-icon-triangle-1-e').addClass('ui-treenode-leaf-icon');
22012                        }
22013                    }
22014                }
22015            }
22016        };
22017
22018        if(this.hasBehavior('filter')) {
22019            this.callBehavior('filter', options);
22020        }
22021        else {
22022            PrimeFaces.ajax.Request.handle(options);
22023        }
22024
22025    },
22026
22027    restoreScrollState: function() {
22028        var scrollState = this.scrollStateHolder.val(),
22029        scrollValues = scrollState.split(',');
22030
22031        this.jq.scrollLeft(scrollValues[0]);
22032        this.jq.scrollTop(scrollValues[1]);
22033    },
22034
22035    saveScrollState: function() {
22036        var scrollState = this.jq.scrollLeft() + ',' + this.jq.scrollTop();
22037
22038        this.scrollStateHolder.val(scrollState);
22039    },
22040
22041    clearScrollState: function() {
22042        this.scrollStateHolder.val('0,0');
22043    }
22044
22045});
22046
22047/**
22048 * PrimeFaces Horizontal Tree Widget
22049 */
22050PrimeFaces.widget.HorizontalTree = PrimeFaces.widget.BaseTree.extend({
22051
22052    init: function(cfg) {
22053        this._super(cfg);
22054
22055        if(PrimeFaces.env.isIE() && !this.cfg.disabled) {
22056            this.drawConnectors();
22057        }
22058    },
22059
22060    //@Override
22061    bindEvents: function() {
22062        var $this = this,
22063        selectionMode = this.cfg.selectionMode,
22064        togglerSelector = '.ui-tree-toggler',
22065        nodeContentSelector = '.ui-treenode-content.ui-tree-selectable';
22066
22067        this.jq.off('click.tree-toggle', togglerSelector)
22068                    .on('click.tree-toggle', togglerSelector, null, function() {
22069                        var icon = $(this),
22070                        node = icon.closest('td.ui-treenode');
22071
22072                        if(node.hasClass('ui-treenode-collapsed'))
22073                            $this.expandNode(node);
22074                        else
22075                            $this.collapseNode(node);
22076                    });
22077
22078        if(selectionMode && this.cfg.highlight) {
22079            this.jq.off('mouseout.tree mouseover.tree', nodeContentSelector)
22080                        .on('mouseover.tree', nodeContentSelector, null, function() {
22081                            var nodeContent = $(this);
22082                            if(!nodeContent.hasClass('ui-state-highlight')) {
22083                                nodeContent.addClass('ui-state-hover');
22084
22085                                if($this.isCheckboxSelection()) {
22086                                    nodeContent.children('div.ui-chkbox').children('div.ui-chkbox-box').addClass('ui-state-hover');
22087                                }
22088                            }
22089                        })
22090                        .on('mouseout.tree', nodeContentSelector, null, function() {
22091                            var nodeContent = $(this);
22092                            if(!nodeContent.hasClass('ui-state-highlight')) {
22093                                nodeContent.removeClass('ui-state-hover');
22094
22095                                if($this.isCheckboxSelection()) {
22096                                    nodeContent.children('div.ui-chkbox').children('div.ui-chkbox-box').removeClass('ui-state-hover');
22097                                }
22098                            }
22099                        });
22100        }
22101
22102        this.jq.off('click.tree-content', nodeContentSelector)
22103                .on('click.tree-content', nodeContentSelector, null, function(e) {
22104                    $this.nodeClick(e, $(this));
22105                });
22106
22107    },
22108
22109    //@Override
22110    showNodeChildren: function(node) {
22111        node.attr('aria-expanded', true);
22112
22113        var childrenContainer = node.next(),
22114        toggleIcon = node.find('> .ui-treenode-content > .ui-tree-toggler'),
22115        nodeType = node.data('nodetype'),
22116        iconState = this.cfg.iconStates[nodeType];
22117
22118        if(iconState) {
22119            toggleIcon.nextAll('span.ui-treenode-icon').removeClass(iconState.collapsedIcon).addClass(iconState.expandedIcon);
22120        }
22121
22122        toggleIcon.addClass('ui-icon-minus').removeClass('ui-icon-plus');
22123        node.removeClass('ui-treenode-collapsed');
22124        childrenContainer.show();
22125
22126        if($.browser.msie) {
22127            this.drawConnectors();
22128        }
22129    },
22130
22131    collapseNode: function(node) {
22132        var childrenContainer = node.next(),
22133        toggleIcon = node.find('> .ui-treenode-content > .ui-tree-toggler'),
22134        nodeType = node.data('nodetype'),
22135        iconState = this.cfg.iconStates[nodeType];
22136
22137        if(iconState) {
22138            toggleIcon.nextAll('span.ui-treenode-icon').removeClass(iconState.expandedIcon).addClass(iconState.collapsedIcon);
22139        }
22140
22141        toggleIcon.removeClass('ui-icon-minus').addClass('ui-icon-plus');
22142        node.addClass('ui-treenode-collapsed');
22143        childrenContainer.hide();
22144
22145        if(this.cfg.dynamic && !this.cfg.cache) {
22146            childrenContainer.children('.ui-treenode-children').empty();
22147        }
22148
22149        if(!this.cfg.cache) {
22150            this.fireCollapseEvent(node);
22151        }
22152
22153        if($.browser.msie) {
22154            this.drawConnectors();
22155        }
22156    },
22157
22158    //@Override
22159    getNodeChildrenContainer: function(node) {
22160        return node.next('.ui-treenode-children-container').children('.ui-treenode-children');
22161    },
22162
22163    selectNode: function(node, silent) {
22164        node.removeClass('ui-treenode-unselected').addClass('ui-treenode-selected').children('.ui-treenode-content').removeClass('ui-state-hover').addClass('ui-state-highlight');
22165
22166        this.addToSelection(this.getRowKey(node));
22167        this.writeSelections();
22168
22169        if(!silent)
22170            this.fireNodeSelectEvent(node);
22171    },
22172
22173    unselectNode: function(node, silent) {
22174        var rowKey = this.getRowKey(node);
22175
22176        node.removeClass('ui-treenode-selected').addClass('ui-treenode-unselected').children('.ui-treenode-content').removeClass('ui-state-highlight');
22177
22178        this.removeFromSelection(rowKey);
22179        this.writeSelections();
22180
22181        if(!silent)
22182            this.fireNodeUnselectEvent(node);
22183    },
22184
22185    unselectAllNodes: function() {
22186        this.selections = [];
22187        this.jq.find('.ui-treenode-content.ui-state-highlight').each(function() {
22188            $(this).removeClass('ui-state-highlight').closest('.ui-treenode').attr('aria-selected', false);
22189        });
22190    },
22191
22192    preselectCheckbox: function() {
22193        var _self = this;
22194
22195        this.jq.find('.ui-chkbox-icon').not('.ui-icon-check').each(function() {
22196            var icon = $(this),
22197            node = icon.closest('.ui-treenode'),
22198            childrenContainer = _self.getNodeChildrenContainer(node);
22199
22200            if(childrenContainer.find('.ui-chkbox-icon.ui-icon-check').length > 0) {
22201                icon.removeClass('ui-icon-blank').addClass('ui-icon-minus');
22202            }
22203        });
22204    },
22205
22206    toggleCheckboxNode: function(node) {
22207        var $this = this,
22208        checkbox = node.find('> .ui-treenode-content > .ui-chkbox'),
22209        checked = checkbox.find('> .ui-chkbox-box > .ui-chkbox-icon').hasClass('ui-icon-check');
22210
22211        this.toggleCheckboxState(checkbox, checked);
22212
22213        if(this.cfg.propagateDown) {
22214            node.next('.ui-treenode-children-container').find('.ui-chkbox').each(function() {
22215                $this.toggleCheckboxState($(this), checked);
22216            });
22217
22218            if(this.cfg.dynamic) {
22219                this.removeDescendantsFromSelection(node.data('rowkey'));
22220            }
22221        }
22222
22223        if(this.cfg.propagateUp) {
22224            node.parents('td.ui-treenode-children-container').each(function() {
22225                var childrenContainer = $(this),
22226                parentNode = childrenContainer.prev('.ui-treenode-parent'),
22227                parentsCheckbox = parentNode.find('> .ui-treenode-content > .ui-chkbox'),
22228                children = childrenContainer.find('> .ui-treenode-children > table > tbody > tr > td.ui-treenode');
22229
22230                if(checked) {
22231                    if(children.filter('.ui-treenode-unselected').length === children.length)
22232                        $this.uncheck(parentsCheckbox);
22233                    else
22234                        $this.partialCheck(parentsCheckbox);
22235                }
22236                else {
22237                    if(children.filter('.ui-treenode-selected').length === children.length)
22238                        $this.check(parentsCheckbox);
22239                    else
22240                        $this.partialCheck(parentsCheckbox);
22241                }
22242            });
22243        }
22244
22245        this.writeSelections();
22246
22247        if(checked)
22248            this.fireNodeUnselectEvent(node);
22249        else
22250            this.fireNodeSelectEvent(node);
22251    },
22252
22253    check: function(checkbox) {
22254        this._super(checkbox);
22255        checkbox.parent('.ui-treenode-content').addClass('ui-state-highlight').removeClass('ui-state-hover');
22256    },
22257
22258    uncheck: function(checkbox) {
22259        this._super(checkbox);
22260        checkbox.parent('.ui-treenode-content').removeClass('ui-state-highlight');
22261    },
22262
22263    drawConnectors: function() {
22264        this.jq.find('table.ui-treenode-connector-table').each(function() {
22265            var table = $(this),
22266            row = table.closest('tr');
22267
22268            table.height(0).height(row.height());
22269        });
22270    },
22271
22272    isEmpty: function() {
22273        return this.jq.children('table').length === 0;
22274    },
22275
22276    focusNode: function(node) {
22277        //focus not supported in horizontal mode
22278    },
22279
22280    //@Override
22281    partialCheck: function(checkbox) {
22282        var box = checkbox.children('.ui-chkbox-box'),
22283        icon = box.children('.ui-chkbox-icon'),
22284        treeNode = checkbox.closest('.ui-treenode'),
22285        rowKey = this.getRowKey(treeNode);
22286
22287        treeNode.find('> .ui-treenode-content').removeClass('ui-state-highlight');
22288        icon.removeClass('ui-icon-blank ui-icon-check').addClass('ui-icon-minus');
22289        treeNode.removeClass('ui-treenode-selected ui-treenode-unselected').addClass('ui-treenode-hasselected').attr('aria-checked', false).attr('aria-selected', false);
22290
22291        this.removeFromSelection(rowKey);
22292     }
22293
22294});
22295/**
22296 * PrimeFaces TreeTable Widget
22297 */
22298PrimeFaces.widget.TreeTable = PrimeFaces.widget.DeferredWidget.extend({
22299
22300    init: function(cfg) {
22301        this._super(cfg);
22302        this.thead = $(this.jqId + '_head');
22303        this.tbody = $(this.jqId + '_data');
22304        this.cfg.expandMode = this.cfg.expandMode||"children";
22305
22306        this.renderDeferred();
22307    },
22308
22309    _render: function() {
22310        if(this.cfg.scrollable) {
22311            this.setupScrolling();
22312        }
22313
22314        if(this.cfg.filter) {
22315            this.setupFiltering();
22316        }
22317
22318        if(this.cfg.resizableColumns) {
22319            this.setupResizableColumns();
22320        }
22321
22322        if(this.cfg.stickyHeader) {
22323            this.setupStickyHeader();
22324        }
22325
22326        if(this.cfg.editable) {
22327            this.bindEditEvents();
22328        }
22329
22330        this.bindEvents();
22331    },
22332
22333    //@override
22334    refresh: function(cfg) {
22335        this.columnWidthsFixed = false;
22336        this.scrollStateVal = this.scrollStateHolder ? this.scrollStateHolder.val() : null;
22337
22338        this._super(cfg);
22339    },
22340
22341    bindEvents: function() {
22342        var $this = this,
22343        togglerSelector = '> tr > td:first-child > .ui-treetable-toggler';
22344
22345        //expand and collapse
22346        this.tbody.off('click.treeTable-toggle', togglerSelector)
22347                    .on('click.treeTable-toggle', togglerSelector, null, function(e) {
22348                        var toggler = $(this),
22349                        node = toggler.closest('tr');
22350
22351                        if(!node.data('processing')) {
22352                            node.data('processing', true);
22353
22354                            if(toggler.hasClass('ui-icon-triangle-1-e'))
22355                                $this.expandNode(node);
22356                            else
22357                                $this.collapseNode(node);
22358                        }
22359                    });
22360
22361        //selection
22362        if(this.cfg.selectionMode) {
22363            this.jqSelection = $(this.jqId + '_selection');
22364            var selectionValue = this.jqSelection.val();
22365            this.selections = selectionValue === "" ? [] : selectionValue.split(',');
22366            this.cfg.disabledTextSelection = this.cfg.disabledTextSelection === false ? false : true;
22367
22368            this.bindSelectionEvents();
22369        }
22370
22371        //sorting
22372        this.bindSortEvents();
22373
22374        if(this.cfg.paginator) {
22375            this.cfg.paginator.paginate = function(newState) {
22376                $this.handlePagination(newState);
22377            };
22378
22379            this.paginator = new PrimeFaces.widget.Paginator(this.cfg.paginator);
22380        }
22381    },
22382
22383    /**
22384     * Binds filter events to standard filters
22385     */
22386    setupFiltering: function() {
22387        var $this = this,
22388        filterColumns = this.thead.find('> tr > th.ui-filter-column');
22389        this.cfg.filterEvent = this.cfg.filterEvent||'keyup';
22390        this.cfg.filterDelay = this.cfg.filterDelay||300;
22391
22392        filterColumns.children('.ui-column-filter').each(function() {
22393            var filter = $(this);
22394
22395            if(filter.is('input:text')) {
22396                PrimeFaces.skinInput(filter);
22397                $this.bindTextFilter(filter);
22398            }
22399            else {
22400                PrimeFaces.skinSelect(filter);
22401                $this.bindChangeFilter(filter);
22402            }
22403        });
22404    },
22405
22406    /**
22407     * Clears table filters
22408     */
22409    clearFilters: function() {
22410        columnFilters = this.thead.find('> tr > th.ui-filter-column > .ui-column-filter').val('');
22411        $(this.jqId + '\\:globalFilter').val('');
22412        this.filter();
22413    },
22414
22415    bindTextFilter: function(filter) {
22416        if(this.cfg.filterEvent === 'enter')
22417            this.bindEnterKeyFilter(filter);
22418        else
22419            this.bindFilterEvent(filter);
22420    },
22421
22422    bindChangeFilter: function(filter) {
22423        var $this = this;
22424
22425        filter.change(function() {
22426            $this.filter();
22427        });
22428    },
22429
22430    bindEnterKeyFilter: function(filter) {
22431        var $this = this;
22432
22433        filter.on('keydown', PrimeFaces.utils.blockEnterKey)
22434        .on('keyup', function(e) {
22435            var key = e.which,
22436            keyCode = $.ui.keyCode;
22437
22438            if(key === keyCode.ENTER) {
22439                $this.filter();
22440
22441                e.preventDefault();
22442            }
22443        });
22444    },
22445
22446    bindFilterEvent: function(filter) {
22447        var $this = this;
22448
22449        //prevent form submit on enter key
22450        filter.on('keydown.treeTable-blockenter', PrimeFaces.utils.blockEnterKey)
22451        .on(this.cfg.filterEvent + '.treeTable', function(e) {
22452            if($this.filterTimeout) {
22453                clearTimeout($this.filterTimeout);
22454            }
22455
22456            $this.filterTimeout = setTimeout(function() {
22457                $this.filter();
22458                $this.filterTimeout = null;
22459            },
22460            $this.cfg.filterDelay);
22461        });
22462    },
22463
22464    /**
22465     * Ajax filter
22466     */
22467    filter: function() {
22468        var $this = this,
22469        options = {
22470            source: this.id,
22471            update: this.id,
22472            process: this.id,
22473            formId: this.cfg.formId,
22474            params: [{name: this.id + '_filtering', value: true},
22475                     {name: this.id + '_encodeFeature', value: true}],
22476            onsuccess: function(responseXML, status, xhr) {
22477                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
22478                        widget: $this,
22479                        handle: function(content) {
22480                            this.tbody.html(content);
22481                        }
22482                    });
22483
22484                return true;
22485            },
22486            oncomplete: function(xhr, status, args, data) {
22487                var paginator = $this.getPaginator();
22488                if(args && args.totalRecords) {
22489                    if(paginator) {
22490                        paginator.setTotalRecords(args.totalRecords);
22491                    }
22492                }
22493            }
22494        };
22495
22496        if(this.hasBehavior('filter')) {
22497            this.callBehavior('filter', options);
22498        }
22499        else {
22500            PrimeFaces.ajax.Request.handle(options);
22501        }
22502    },
22503
22504    handlePagination: function(newState) {
22505        var $this = this,
22506        options = {
22507            source: this.id,
22508            update: this.id,
22509            process: this.id,
22510            params: [
22511                {name: this.id + '_pagination', value: true},
22512                {name: this.id + '_first', value: newState.first},
22513                {name: this.id + '_rows', value: newState.rows}
22514            ],
22515            onsuccess: function(responseXML, status, xhr) {
22516                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
22517                        widget: $this,
22518                        handle: function(content) {
22519                            this.tbody.html(content);
22520                        }
22521                    });
22522
22523                return true;
22524            },
22525            oncomplete: function() {
22526                $this.paginator.cfg.page = newState.page;
22527                $this.paginator.updateUI();
22528            }
22529        };
22530
22531        if(this.hasBehavior('page')) {
22532            this.callBehavior('page', options);
22533        }
22534        else {
22535            PrimeFaces.ajax.Request.handle(options);
22536        }
22537    },
22538
22539    getPaginator: function() {
22540        return this.paginator;
22541    },
22542
22543    bindSelectionEvents: function() {
22544        var $this = this,
22545        rowSelector = '> tr.ui-treetable-selectable-node';
22546
22547        this.tbody.off('mouseover.treeTable mouseout.treeTable click.treeTable', rowSelector)
22548                    .on('mouseover.treeTable', rowSelector, null, function(e) {
22549                        var element = $(this);
22550                        if(!element.hasClass('ui-state-highlight')) {
22551                            element.addClass('ui-state-hover');
22552
22553                            if($this.isCheckboxSelection() && !$this.cfg.nativeElements) {
22554                                element.find('> td:first-child > div.ui-chkbox > div.ui-chkbox-box').addClass('ui-state-hover');
22555                            }
22556                        }
22557                    })
22558                    .on('mouseout.treeTable', rowSelector, null, function(e) {
22559                        var element = $(this);
22560                        if(!element.hasClass('ui-state-highlight')) {
22561                            element.removeClass('ui-state-hover');
22562
22563                            if($this.isCheckboxSelection() && !$this.cfg.nativeElements) {
22564                                element.find('> td:first-child > div.ui-chkbox > div.ui-chkbox-box').removeClass('ui-state-hover');
22565                            }
22566                        }
22567                    })
22568                    .on('click.treeTable', rowSelector, null, function(e) {
22569                        $this.onRowClick(e, $(this));
22570                    });
22571
22572        if(this.isCheckboxSelection()) {
22573           var checkboxSelector =  this.cfg.nativeElements ? '> tr.ui-treetable-selectable-node > td:first-child :checkbox':
22574                    '> tr.ui-treetable-selectable-node > td:first-child div.ui-chkbox-box';
22575
22576                this.tbody.off('click.treeTable-checkbox', checkboxSelector)
22577                      .on('click.treeTable-checkbox', checkboxSelector, null, function(e) {
22578                          var node = $(this).closest('tr.ui-treetable-selectable-node');
22579                          $this.toggleCheckboxNode(node);
22580                      });
22581
22582
22583                //initial partial selected visuals
22584                if(this.cfg.nativeElements) {
22585                    this.indeterminateNodes(this.tbody.children('tr.ui-treetable-partialselected'));
22586                }
22587        }
22588    },
22589
22590    bindSortEvents: function() {
22591        var $this = this;
22592        this.sortableColumns = this.thead.find('> tr > th.ui-sortable-column');
22593
22594        this.sortableColumns.filter('.ui-state-active').each(function() {
22595            var columnHeader = $(this),
22596            sortIcon = columnHeader.children('span.ui-sortable-column-icon'),
22597            sortOrder = null;
22598
22599            if(sortIcon.hasClass('ui-icon-triangle-1-n'))
22600                sortOrder = 'ASCENDING';
22601            else
22602                sortOrder = 'DESCENDING';
22603
22604            columnHeader.data('sortorder', sortOrder);
22605        });
22606
22607        this.sortableColumns.on('mouseenter.treeTable', function() {
22608            var column = $(this);
22609
22610            if(!column.hasClass('ui-state-active'))
22611                column.addClass('ui-state-hover');
22612        })
22613        .on('mouseleave.treeTable', function() {
22614            var column = $(this);
22615
22616            if(!column.hasClass('ui-state-active'))
22617                column.removeClass('ui-state-hover');
22618        })
22619        .on('click.treeTable', function(e) {
22620            if(!$this.shouldSort(e, this)) {
22621                return;
22622            }
22623
22624            PrimeFaces.clearSelection();
22625
22626            var columnHeader = $(this),
22627            sortOrder = columnHeader.data('sortorder')||'DESCENDING';
22628
22629            if(sortOrder === 'ASCENDING')
22630                sortOrder = 'DESCENDING';
22631            else if(sortOrder === 'DESCENDING')
22632                sortOrder = 'ASCENDING';
22633
22634            $this.sort(columnHeader, sortOrder);
22635        });
22636    },
22637
22638    bindContextMenu : function(menuWidget, targetWidget, targetId, cfg) {
22639        var targetSelector = targetId + ' .ui-treetable-data > ' + (cfg.nodeType ? 'tr.ui-treetable-selectable-node.' + cfg.nodeType : 'tr.ui-treetable-selectable-node');
22640        var targetEvent = cfg.event + '.treetable';
22641
22642        $(document).off(targetEvent, targetSelector).on(targetEvent, targetSelector, null, function(e) {
22643            targetWidget.onRowRightClick(e, $(this));
22644            menuWidget.show(e);
22645        });
22646    },
22647
22648    setupStickyHeader: function() {
22649        var table = this.thead.parent(),
22650        offset = table.offset(),
22651        win = $(window),
22652        $this = this;
22653
22654        this.stickyContainer = $('<div class="ui-treetable ui-treetable-sticky ui-widget"><table></table></div>');
22655        this.clone = this.thead.clone(false);
22656        this.stickyContainer.children('table').append(this.thead);
22657        table.append(this.clone);
22658
22659        this.stickyContainer.css({
22660            position: 'absolute',
22661            width: table.outerWidth(),
22662            top: offset.top,
22663            left: offset.left,
22664            'z-index': ++PrimeFaces.zindex
22665        });
22666
22667        this.jq.prepend(this.stickyContainer);
22668
22669        if(this.cfg.resizableColumns) {
22670            this.relativeHeight = 0;
22671        }
22672
22673        PrimeFaces.utils.registerScrollHandler(this, 'scroll.' + this.id + '_align', function() {
22674            var scrollTop = win.scrollTop(),
22675            tableOffset = table.offset();
22676
22677            if(scrollTop > tableOffset.top) {
22678                $this.stickyContainer.css({
22679                                        'position': 'fixed',
22680                                        'top': '0px'
22681                                    })
22682                                    .addClass('ui-shadow ui-sticky');
22683
22684                if($this.cfg.resizableColumns) {
22685                    $this.relativeHeight = scrollTop - tableOffset.top;
22686                }
22687
22688                if(scrollTop >= (tableOffset.top + $this.tbody.height()))
22689                    $this.stickyContainer.hide();
22690                else
22691                    $this.stickyContainer.show();
22692            }
22693            else {
22694                $this.stickyContainer.css({
22695                                        'position': 'absolute',
22696                                        'top': tableOffset.top
22697                                    })
22698                                    .removeClass('ui-shadow ui-sticky');
22699
22700                if($this.stickyContainer.is(':hidden')) {
22701                    $this.stickyContainer.show();
22702                }
22703
22704                if($this.cfg.resizableColumns) {
22705                    $this.relativeHeight = 0;
22706                }
22707            }
22708        });
22709
22710        PrimeFaces.utils.registerResizeHandler(this, 'resize.sticky-' + this.id + '_align', null, function() {
22711            $this.stickyContainer.width(table.outerWidth());
22712        });
22713    },
22714
22715    bindEditEvents: function() {
22716        var $this = this;
22717        this.cfg.cellSeparator = this.cfg.cellSeparator||' ';
22718
22719        if(this.cfg.editMode === 'row') {
22720            var rowEditorSelector = '> tr > td > div.ui-row-editor';
22721            this.tbody.off('click.treetable', rowEditorSelector)
22722                        .on('click.treetable', rowEditorSelector, null, function(e) {
22723                            var element = $(e.target),
22724                            row = element.closest('tr');
22725
22726                            if(element.hasClass('ui-icon-pencil')) {
22727                                $this.switchToRowEdit(row);
22728                                element.hide().siblings().show();
22729                            }
22730                            else if(element.hasClass('ui-icon-check')) {
22731                                $this.saveRowEdit(row);
22732                            }
22733                            else if(element.hasClass('ui-icon-close')) {
22734                                $this.cancelRowEdit(row);
22735                            }
22736
22737                            e.preventDefault();
22738                        });
22739        }
22740        else if(this.cfg.editMode === 'cell') {
22741            var cellSelector = '> tr > td.ui-editable-column';
22742
22743            this.tbody.off('click.treetable-cell', cellSelector)
22744                        .on('click.treetable-cell', cellSelector, null, function(e) {
22745                            if(!$(e.target).is('span.ui-treetable-toggler.ui-c')) {
22746                                $this.incellClick = true;
22747
22748                                var cell = $(this);
22749                                if(!cell.hasClass('ui-cell-editing')) {
22750                                    $this.showCellEditor($(this));
22751                                }
22752                            }
22753                        });
22754
22755            $(document).off('click.treetable-cell-blur' + this.id)
22756                        .on('click.treetable-cell-blur' + this.id, function(e) {
22757                            if((!$this.incellClick && $this.currentCell && !$this.contextMenuClick)) {
22758                                $this.saveCell($this.currentCell);
22759                            }
22760
22761                            $this.incellClick = false;
22762                            $this.contextMenuClick = false;
22763                        });
22764        }
22765    },
22766
22767    sort: function(columnHeader, order) {
22768        var $this = this,
22769        options = {
22770            source: this.id,
22771            update: this.id,
22772            process: this.id,
22773            params: [
22774                {name: this.id + '_sorting', value: true},
22775                {name: this.id + '_sortKey', value: columnHeader.attr('id')},
22776                {name: this.id + '_sortDir', value: order}
22777            ],
22778            onsuccess: function(responseXML, status, xhr) {
22779                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
22780                        widget: $this,
22781                        handle: function(content) {
22782                            this.tbody.html(content);
22783
22784                            columnHeader.siblings().filter('.ui-state-active').removeData('sortorder').removeClass('ui-state-active')
22785                                            .find('.ui-sortable-column-icon').removeClass('ui-icon-triangle-1-n ui-icon-triangle-1-s');
22786
22787                            columnHeader.removeClass('ui-state-hover').addClass('ui-state-active').data('sortorder', order);
22788                            var sortIcon = columnHeader.find('.ui-sortable-column-icon');
22789
22790                            if(order === 'DESCENDING')
22791                                sortIcon.removeClass('ui-icon-triangle-1-n').addClass('ui-icon-triangle-1-s');
22792                            else if(order === 'ASCENDING')
22793                                sortIcon.removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-n');
22794                        }
22795                    });
22796
22797                return true;
22798            },
22799            oncomplete: function(xhr, status, args, data) {
22800                if($this.cfg.selectionMode && args.selection) {
22801                    $this.selections = args.selection.split(',');
22802                    $this.writeSelections();
22803                }
22804            }
22805        };
22806
22807        if(this.hasBehavior('sort')) {
22808            this.callBehavior('sort', options);
22809        }
22810        else {
22811            PrimeFaces.ajax.Request.handle(options);
22812        }
22813    },
22814
22815    expandNode: function(node) {
22816        var $this = this,
22817        nodeKey = node.attr('data-rk'),
22818        options = {
22819            source: this.id,
22820            process: this.id,
22821            update: this.id,
22822            params: [
22823                {name: this.id + '_expand', value: nodeKey}
22824            ],
22825            onsuccess: function(responseXML, status, xhr) {
22826                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
22827                        widget: $this,
22828                        handle: function(content) {
22829                            if($this.cfg.expandMode === "self")
22830                                node.replaceWith(content);
22831                            else
22832                                node.after(content);
22833
22834                            node.find('.ui-treetable-toggler:first').addClass('ui-icon-triangle-1-s').removeClass('ui-icon-triangle-1-e');
22835                            node.attr('aria-expanded', true);
22836                            $this.indeterminateNodes($this.tbody.children('tr.ui-treetable-partialselected'));
22837
22838                            if(this.cfg.scrollable) {
22839                                this.alignScrollBody();
22840                            }
22841                        }
22842                    });
22843
22844                return true;
22845            },
22846            oncomplete: function() {
22847                node.data('processing', false);
22848                $this.updateVerticalScroll();
22849            }
22850        };
22851
22852        if(this.hasBehavior('expand')) {
22853            this.callBehavior('expand', options);
22854        }
22855        else {
22856            PrimeFaces.ajax.Request.handle(options);
22857        }
22858    },
22859
22860    collapseNode: function(node) {
22861        var nodeKey = node.attr('data-rk'),
22862        nextNodes = node.nextAll();
22863
22864        for(var i = 0; i < nextNodes.length; i++) {
22865            var nextNode = nextNodes.eq(i),
22866            nextNodeRowKey = nextNode.attr('data-rk');
22867
22868            if(nextNodeRowKey.indexOf(nodeKey) !== -1) {
22869               nextNode.remove();
22870            }
22871            else {
22872                break;
22873            }
22874        }
22875
22876        node.attr('aria-expanded', false).find('.ui-treetable-toggler:first').addClass('ui-icon-triangle-1-e').removeClass('ui-icon-triangle-1-s');
22877        node.data('processing', false);
22878
22879        if(this.cfg.scrollable) {
22880            this.alignScrollBody();
22881        }
22882
22883        if(this.hasBehavior('collapse')) {
22884            var $this = this,
22885            nodeKey = node.attr('data-rk'),
22886            options = {
22887                source: this.id,
22888                process: this.id,
22889                update: this.id,
22890                params: [
22891                    {name: this.id + '_collapse', value: nodeKey}
22892                ],
22893                onsuccess: function(responseXML, status, xhr) {
22894                    PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
22895                            widget: $this,
22896                            handle: function(content) {
22897                                // do nothing
22898                            }
22899                        });
22900
22901                    return true;
22902                },
22903                oncomplete: function() {
22904                    $this.updateVerticalScroll();
22905                }
22906            };
22907
22908            this.callBehavior('collapse', options);
22909        }
22910        else {
22911            this.updateVerticalScroll();
22912        }
22913    },
22914
22915    onRowClick: function(event, node) {
22916        if($(event.target).is('td,span:not(.ui-c)')) {
22917            var selected = node.hasClass('ui-state-highlight'),
22918            metaKey = event.metaKey||event.ctrlKey,
22919            shiftKey = event.shiftKey;
22920
22921            if(this.isCheckboxSelection()) {
22922                this.toggleCheckboxNode(node);
22923            }
22924            else {
22925                if(selected && metaKey) {
22926                    this.unselectNode(node);
22927                }
22928                else {
22929                    if(this.isSingleSelection()||(this.isMultipleSelection() && !metaKey)) {
22930                        this.unselectAllNodes();
22931                    }
22932
22933                    if(this.isMultipleSelection() && shiftKey) {
22934                        this.selectNodesInRange(node);
22935                    }
22936                    else {
22937                        this.selectNode(node);
22938                        this.cursorNode = node;
22939                    }
22940                }
22941            }
22942
22943            if(this.cfg.disabledTextSelection) {
22944                PrimeFaces.clearSelection();
22945            }
22946        }
22947    },
22948
22949    onRowRightClick: function(event, node) {
22950        var selected = node.hasClass('ui-state-highlight');
22951
22952        if(this.isCheckboxSelection()) {
22953            if(!selected) {
22954                this.toggleCheckboxNode(node);
22955            }
22956        }
22957        else {
22958            if(this.isSingleSelection() || !selected ) {
22959                this.unselectAllNodes();
22960            }
22961            this.selectNode(node);
22962        }
22963
22964        if(this.cfg.disabledTextSelection) {
22965            PrimeFaces.clearSelection();
22966        }
22967    },
22968
22969    selectNode: function(node, silent) {
22970        var nodeKey = node.attr('data-rk');
22971
22972        node.removeClass('ui-state-hover ui-treetable-partialselected').addClass('ui-state-highlight').attr('aria-selected', true);
22973        this.addToSelection(nodeKey);
22974        this.writeSelections();
22975
22976        if(this.isCheckboxSelection()) {
22977            if(this.cfg.nativeElements)
22978                node.find('> td:first-child > :checkbox').prop('checked', true).prop('indeterminate', false);
22979            else
22980                node.find('> td:first-child > div.ui-chkbox > div.ui-chkbox-box').removeClass('ui-state-hover').children('span.ui-chkbox-icon').removeClass('ui-icon-blank ui-icon-minus').addClass('ui-icon-check');
22981        }
22982
22983        if(!silent) {
22984            this.fireSelectNodeEvent(nodeKey);
22985        }
22986    },
22987
22988    unselectNode: function(node, silent) {
22989        var nodeKey = node.attr('data-rk');
22990
22991        node.removeClass('ui-state-highlight ui-treetable-partialselected').attr('aria-selected', false);
22992        this.removeSelection(nodeKey);
22993        this.writeSelections();
22994
22995        if(this.isCheckboxSelection()) {
22996            if(this.cfg.nativeElements)
22997                node.find('> td:first-child > :checkbox').prop('checked', false).prop('indeterminate', false);
22998            else
22999                node.find('> td:first-child > div.ui-chkbox > div.ui-chkbox-box > span.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check ui-icon-minus');
23000        }
23001
23002        if(!silent) {
23003            this.fireUnselectNodeEvent(nodeKey);
23004        }
23005    },
23006
23007    unselectAllNodes: function() {
23008        var selectedNodes = this.tbody.children('tr.ui-state-highlight');
23009        for(var i = 0; i < selectedNodes.length; i++) {
23010            this.unselectNode(selectedNodes.eq(i), true);
23011        }
23012
23013        this.selections = [];
23014        this.writeSelections();
23015    },
23016
23017    selectNodesInRange: function(node) {
23018        if(this.cursorNode) {
23019            this.unselectAllNodes();
23020
23021            var currentNodeIndex = node.index(),
23022            cursorNodeIndex = this.cursorNode.index(),
23023            startIndex = (currentNodeIndex > cursorNodeIndex) ? cursorNodeIndex : currentNodeIndex,
23024            endIndex = (currentNodeIndex > cursorNodeIndex) ? (currentNodeIndex + 1) : (cursorNodeIndex + 1),
23025            nodes = this.tbody.children();
23026
23027            for(var i = startIndex ; i < endIndex; i++) {
23028                this.selectNode(nodes.eq(i), true);
23029            }
23030        }
23031        else {
23032            this.selectNode(node);
23033        }
23034    },
23035
23036    indeterminateNodes: function(nodes) {
23037        for(var i = 0; i < nodes.length; i++) {
23038            nodes.eq(i).find('> td:first-child > :checkbox').prop('indeterminate', true);
23039        }
23040    },
23041
23042    toggleCheckboxNode: function(node) {
23043        var selected = node.hasClass('ui-state-highlight'),
23044        rowKey = node.data('rk');
23045
23046        //toggle itself
23047        if(selected)
23048            this.unselectNode(node, true);
23049        else
23050            this.selectNode(node, true);
23051
23052        //propagate down
23053        var descendants = this.getDescendants(node);
23054        for(var i = 0;
23054 i < descendants.length; i++) {
23055            var descendant = descendants[i];
23056            if(selected)
23057                this.unselectNode(descendant, true);
23058            else
23059                this.selectNode(descendant, true);
23060        }
23061
23062        if(selected) {
23063           this.removeDescendantsFromSelection(node.data('rk'));
23064        }
23065
23066        //propagate up
23067        var parentNode = this.getParent(node);
23068        if(parentNode) {
23069            this.propagateUp(parentNode);
23070        }
23071
23072        this.writeSelections();
23073
23074        if(selected)
23075            this.fireUnselectNodeEvent(rowKey);
23076        else
23077            this.fireSelectNodeEvent(rowKey);
23078    },
23079
23080    getDescendants: function(node) {
23081        var nodeKey = node.attr('data-rk'),
23082        nextNodes = node.nextAll(),
23083        descendants = [];
23084
23085        for(var i = 0; i < nextNodes.length; i++) {
23086            var nextNode = nextNodes.eq(i),
23087            nextNodeRowKey = nextNode.attr('data-rk');
23088
23089            if(nextNodeRowKey.indexOf(nodeKey) != -1) {
23090                descendants.push(nextNode);
23091            }
23092            else {
23093                break;
23094            }
23095        }
23096
23097        return descendants;
23098    },
23099
23100    getChildren: function(node) {
23101        var nodeKey = node.attr('data-rk'),
23102        nextNodes = node.nextAll(),
23103        children = [];
23104
23105        for(var i = 0; i < nextNodes.length; i++) {
23106            var nextNode = nextNodes.eq(i),
23107            nextNodeParentKey = nextNode.attr('data-prk');
23108
23109            if(nextNodeParentKey === nodeKey) {
23110                children.push(nextNode);
23111            }
23112        }
23113
23114        return children;
23115    },
23116
23117    propagateUp: function(node) {
23118        var children = this.getChildren(node),
23119        allSelected = true,
23120        partialSelected = false,
23121        checkbox = this.cfg.nativeElements ? node.find('> td:first-child > :checkbox') :
23122                            node.find('> td:first-child > div.ui-chkbox > div.ui-chkbox-box > span.ui-chkbox-icon');
23123
23124        for(var i = 0; i < children.length; i++) {
23125            var child = children[i],
23126            childSelected = child.hasClass('ui-state-highlight');
23127
23128            allSelected = allSelected&&childSelected;
23129            partialSelected = partialSelected||childSelected||child.hasClass('ui-treetable-partialselected');
23130        }
23131
23132        if(allSelected) {
23133            node.removeClass('ui-treetable-partialselected');
23134            this.selectNode(node, true);
23135        }
23136        else if(partialSelected) {
23137            node.removeClass('ui-state-highlight').addClass('ui-treetable-partialselected');
23138
23139            if(this.cfg.nativeElements)
23140                checkbox.prop('indeterminate', true);
23141            else
23142                checkbox.removeClass('ui-icon-blank ui-icon-check').addClass('ui-icon-minus');
23143
23144            this.removeSelection(node.attr('data-rk'));
23145        }
23146        else {
23147            node.removeClass('ui-state-highlight ui-treetable-partialselected');
23148
23149            if(this.cfg.nativeElements)
23150                checkbox.prop('indeterminate', false).prop('checked', false);
23151            else
23152                checkbox.addClass('ui-icon-blank').removeClass('ui-icon-check ui-icon-minus');
23153
23154            this.removeSelection(node.attr('data-rk'));
23155        }
23156
23157        var parent = this.getParent(node);
23158        if(parent) {
23159            this.propagateUp(parent);
23160        }
23161    },
23162
23163    getParent: function(node) {
23164        var parent = $(this.jqId + '_node_' + node.attr('data-prk'));
23165
23166        return parent.length === 1 ? parent : null;
23167    },
23168
23169    removeDescendantsFromSelection: function(rowKey) {
23170        this.selections = $.grep(this.selections, function(value) {
23171            return value.indexOf(rowKey + '_') !== 0;
23172        });
23173    },
23174
23175    removeSelection: function(nodeKey) {
23176        this.selections = $.grep(this.selections, function(value) {
23177            return value !== nodeKey;
23178        });
23179    },
23180
23181    addToSelection: function(rowKey) {
23182        if(!this.isSelected(rowKey)) {
23183            this.selections.push(rowKey);
23184        }
23185    },
23186
23187    isSelected: function(nodeKey) {
23188        return PrimeFaces.inArray(this.selections, nodeKey);
23189    },
23190
23191    isSingleSelection: function() {
23192        return this.cfg.selectionMode == 'single';
23193    },
23194
23195    isMultipleSelection: function() {
23196        return this.cfg.selectionMode == 'multiple';
23197    },
23198
23199    isCheckboxSelection: function() {
23200        return this.cfg.selectionMode == 'checkbox';
23201    },
23202
23203    writeSelections: function() {
23204        this.jqSelection.val(this.selections.join(','));
23205    },
23206
23207    fireSelectNodeEvent: function(nodeKey) {
23208        if(this.isCheckboxSelection()) {
23209            var $this = this,
23210            options = {
23211                source: this.id,
23212                process: this.id
23213            };
23214
23215            options.params = [
23216                {name: this.id + '_instantSelection', value: nodeKey}
23217            ];
23218
23219            options.oncomplete = function(xhr, status, args, data) {
23220                if(args.descendantRowKeys && args.descendantRowKeys !== '') {
23221                    var rowKeys = args.descendantRowKeys.split(',');
23222                    for(var i = 0; i < rowKeys.length; i++) {
23223                        $this.addToSelection(rowKeys[i]);
23224                    }
23225                    $this.writeSelections();
23226                }
23227            }
23228
23229            if(this.hasBehavior('select')) {
23230                this.callBehavior('select', options);
23231            }
23232            else {
23233                PrimeFaces.ajax.Request.handle(options);
23234            }
23235        }
23236        else {
23237            if(this.hasBehavior('select')) {
23238                var ext = {
23239                    params: [
23240                        {name: this.id + '_instantSelection', value: nodeKey}
23241                    ]
23242                };
23243
23244                this.callBehavior('select', ext);
23245            }
23246        }
23247    },
23248
23249    fireUnselectNodeEvent: function(nodeKey) {
23250        if(this.hasBehavior('unselect')) {
23251            var ext = {
23252                params: [
23253                    {name: this.id + '_instantUnselection', value: nodeKey}
23254                ]
23255            };
23256
23257            this.callBehavior('unselect', ext);
23258        }
23259    },
23260
23261    setupScrolling: function() {
23262        this.scrollHeader = this.jq.children('div.ui-treetable-scrollable-header');
23263        this.scrollBody = this.jq.children('div.ui-treetable-scrollable-body');
23264        this.scrollFooter = this.jq.children('div.ui-treetable-scrollable-footer');
23265        this.scrollStateHolder = $(this.jqId + '_scrollState');
23266        this.scrollHeaderBox = this.scrollHeader.children('div.ui-treetable-scrollable-header-box');
23267        this.scrollFooterBox = this.scrollFooter.children('div.ui-treetable-scrollable-footer-box');
23268        this.headerTable = this.scrollHeaderBox.children('table');
23269        this.bodyTable = this.scrollBody.children('table');
23270        this.footerTable = this.scrollFooterBox.children('table');
23271        this.headerCols = this.headerTable.find('> thead > tr > th');
23272        this.footerCols = this.footerTable.find('> tfoot > tr > td');
23273        this.percentageScrollHeight = this.cfg.scrollHeight && (this.cfg.scrollHeight.indexOf('%') !== -1);
23274        this.percentageScrollWidth = this.cfg.scrollWidth && (this.cfg.scrollWidth.indexOf('%') !== -1);
23275        var $this = this;
23276
23277        if(this.cfg.scrollHeight) {
23278            if(this.cfg.scrollHeight.indexOf('%') !== -1) {
23279                this.adjustScrollHeight();
23280            }
23281
23282            if(this.cfg.scrollHeight.indexOf('vh') !== -1)  {
23283                this.applyViewPortScrollHeight();
23284            }
23285
23286            this.marginRight = this.getScrollbarWidth() + 'px';
23287            this.scrollHeaderBox.css('margin-right', this.marginRight);
23288            this.scrollFooterBox.css('margin-right', this.marginRight);
23289            this.alignScrollBody();
23290        }
23291
23292        this.fixColumnWidths();
23293
23294        if(this.cfg.scrollWidth) {
23295            if(this.cfg.scrollWidth.indexOf('%') !== -1) {
23296                this.adjustScrollWidth();
23297            }
23298            else {
23299                this.setScrollWidth(parseInt(this.cfg.scrollWidth));
23300            }
23301        }
23302
23303        this.cloneHead();
23304
23305        this.restoreScrollState();
23306
23307        this.updateVerticalScroll();
23308
23309        this.scrollBody.scroll(function() {
23310            var scrollLeft = $this.scrollBody.scrollLeft();
23311            $this.scrollHeaderBox.css('margin-left', -scrollLeft);
23312            $this.scrollFooterBox.css('margin-left', -scrollLeft);
23313
23314            $this.saveScrollState();
23315        });
23316
23317         this.scrollHeader.on('scroll.treeTable', function() {
23318            $this.scrollHeader.scrollLeft(0);
23319        });
23320
23321        this.scrollFooter.on('scroll.treeTable', function() {
23322            $this.scrollFooter.scrollLeft(0);
23323        });
23324
23325        PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_align', $this.jq, function() {
23326            if ($this.percentageScrollHeight) {
23327                $this.adjustScrollHeight();
23328            }
23329            if ($this.percentageScrollWidth) {
23330                $this.adjustScrollWidth();
23331            }
23332        });
23333    },
23334
23335    cloneHead: function() {
23336        this.theadClone = this.headerTable.children('thead').clone();
23337        this.theadClone.find('th').each(function() {
23338            var header = $(this);
23339            header.attr('id', header.attr('id') + '_clone');
23340        });
23341        this.theadClone.removeAttr('id').addClass('ui-treetable-scrollable-theadclone').height(0).prependTo(this.bodyTable);
23342    },
23343
23344     fixColumnWidths: function() {
23345        var $this = this;
23346
23347        if(!this.columnWidthsFixed) {
23348            if(this.cfg.scrollable) {
23349                this.headerCols.each(function() {
23350                    var headerCol = $(this),
23351                    colIndex = headerCol.index(),
23352                    width = headerCol.width();
23353
23354                    headerCol.width(width);
23355
23356                    if($this.footerCols.length > 0) {
23357                        var footerCol = $this.footerCols.eq(colIndex);
23358                        footerCol.width(width);
23359                    }
23360                });
23361            }
23362            else {
23363                this.jq.find('> table > thead > tr > th').each(function() {
23364                    var col = $(this);
23365                    col.width(col.width());
23366                });
23367            }
23368
23369            this.columnWidthsFixed = true;
23370        }
23371    },
23372
23373    updateColumnWidths: function() {
23374        this.columnWidthsFixed = false;
23375        this.jq.find('> table > thead > tr > th').each(function() {
23376            var col = $(this);
23377            col.css('width', '');
23378        });
23379        this.fixColumnWidths();
23380    },
23381
23382    adjustScrollHeight: function() {
23383        var relativeHeight = this.jq.parent().innerHeight() * (parseInt(this.cfg.scrollHeight) / 100),
23384        tableHeaderHeight = this.jq.children('.ui-treetable-header').outerHeight(true),
23385        tableFooterHeight = this.jq.children('.ui-treetable-footer').outerHeight(true),
23386        scrollersHeight = (this.scrollHeader.outerHeight(true) + this.scrollFooter.outerHeight(true)),
23387        height = (relativeHeight - (scrollersHeight + tableHeaderHeight + tableFooterHeight));
23388
23389        this.scrollBody.height(height);
23390    },
23391
23392    applyViewPortScrollHeight: function() {
23393        this.scrollBody.height(this.cfg.scrollHeight);
23394    },
23395
23396    adjustScrollWidth: function() {
23397        var width = parseInt((this.jq.parent().innerWidth() * (parseInt(this.cfg.scrollWidth) / 100)));
23398        this.setScrollWidth(width);
23399    },
23400
23401    setOuterWidth: function(element, width) {
23402        var diff = element.outerWidth() - element.width();
23403        element.width(width - diff);
23404    },
23405
23406    hasVerticalOverflow: function() {
23407        return (this.cfg.scrollHeight && this.bodyTable.outerHeight() > this.scrollBody.outerHeight());
23408    },
23409
23410    setScrollWidth: function(width) {
23411        var $this = this;
23412        this.jq.children('.ui-widget-header').each(function() {
23413            $this.setOuterWidth($(this), width);
23414        });
23415        this.scrollHeader.width(width);
23416        this.scrollBody.css('padding-right', 0).width(width);
23417        this.scrollFooter.width(width);
23418    },
23419
23420    alignScrollBody: function() {
23421        if(!this.cfg.scrollWidth) {
23422            if(this.hasVerticalOverflow())
23423                this.scrollBody.css('padding-right', 0);
23424            else
23425                this.scrollBody.css('padding-right', this.getScrollbarWidth());
23426        }
23427    },
23428
23429    getScrollbarWidth: function() {
23430        return $.browser.webkit ? '15' : PrimeFaces.calculateScrollbarWidth();
23431    },
23432
23433    restoreScrollState: function() {
23434        var scrollState = this.scrollStateVal||this.scrollStateHolder.val(),
23435        scrollValues = scrollState.split(',');
23436
23437        this.scrollBody.scrollLeft(scrollValues[0]);
23438        this.scrollBody.scrollTop(scrollValues[1]);
23439        this.scrollStateVal = null;
23440    },
23441
23442    saveScrollState: function() {
23443        var scrollState = this.scrollBody.scrollLeft() + ',' + this.scrollBody.scrollTop();
23444
23445        this.scrollStateHolder.val(scrollState);
23446    },
23447
23448    setupResizableColumns: function() {
23449        this.fixColumnWidths();
23450
23451        if(!this.cfg.liveResize) {
23452            this.resizerHelper = $('<div class="ui-column-resizer-helper ui-state-highlight"></div>').appendTo(this.jq);
23453        }
23454
23455        this.thead.find('> tr > th.ui-resizable-column:not(:last-child)').prepend('<span class="ui-column-resizer">&nbsp;</span>');
23456        var resizers = this.thead.find('> tr > th > span.ui-column-resizer'),
23457        $this = this;
23458
23459        resizers.draggable({
23460            axis: 'x',
23461            start: function() {
23462                if($this.cfg.liveResize) {
23463                    $this.jq.css('cursor', 'col-resize');
23464                }
23465                else {
23466                    var header = $this.cfg.stickyHeader ? $this.clone : $this.thead,
23467                        height = $this.cfg.scrollable ? $this.scrollBody.height() : header.parent().height() - header.height() - 1;
23468
23469                    if($this.cfg.stickyHeader) {
23470                        height = height - $this.relativeHeight;
23471                    }
23472
23473                    $this.resizerHelper.height(height);
23474                    $this.resizerHelper.show();
23475                }
23476            },
23477            drag: function(event, ui) {
23478                if($this.cfg.liveResize) {
23479                    $this.resize(event, ui);
23480                }
23481                else {
23482                    $this.resizerHelper.offset({
23483                        left: ui.helper.offset().left + ui.helper.width() / 2,
23484                        top: $this.thead.offset().top + $this.thead.height()
23485                    });
23486                }
23487            },
23488            stop: function(event, ui) {
23489                var columnHeader = ui.helper.parent();
23490                ui.helper.css('left','');
23491
23492                if($this.cfg.liveResize) {
23493                    $this.jq.css('cursor', 'default');
23494                } else {
23495                    $this.resize(event, ui);
23496                    $this.resizerHelper.hide();
23497                }
23498
23499                var options = {
23500                    source: $this.id,
23501                    process: $this.id,
23502                    params: [
23503                        {name: $this.id + '_colResize', value: true},
23504                        {name: $this.id + '_columnId', value: columnHeader.attr('id')},
23505                        {name: $this.id + '_width', value: parseInt(columnHeader.width())},
23506                        {name: $this.id + '_height', value: parseInt(columnHeader.height())}
23507                    ]
23508                };
23509
23510                if($this.hasBehavior('colResize')) {
23511                    $this.callBehavior('colResize', options);
23512                }
23513
23514                if($this.cfg.stickyHeader) {
23515                    $this.reclone();
23516                }
23517            },
23518            containment: this.jq
23519        });
23520    },
23521
23522    resize: function(event, ui) {
23523        var columnHeader = ui.helper.parent(),
23524        nextColumnHeader = columnHeader.next(),
23525        change = null, newWidth = null, nextColumnWidth = null;
23526
23527        if(this.cfg.liveResize) {
23528            change = columnHeader.outerWidth() - (event.pageX - columnHeader.offset().left),
23529            newWidth = (columnHeader.width() - change),
23530            nextColumnWidth = (nextColumnHeader.width() + change);
23531        }
23532        else {
23533            change = (ui.position.left - ui.originalPosition.left),
23534            newWidth = (columnHeader.width() + change),
23535            nextColumnWidth = (nextColumnHeader.width() - change);
23536        }
23537
23538        if(newWidth > 15 && nextColumnWidth > 15) {
23539            columnHeader.width(newWidth);
23540            nextColumnHeader.width(nextColumnWidth);
23541            var colIndex = columnHeader.index();
23542
23543            if(this.cfg.scrollable) {
23544                this.theadClone.find(PrimeFaces.escapeClientId(columnHeader.attr('id') + '_clone')).width(newWidth);
23545                this.theadClone.find(PrimeFaces.escapeClientId(nextColumnHeader.attr('id') + '_clone')).width(nextColumnWidth);
23546
23547                if(this.footerCols.length > 0) {
23548                    var footerCol = this.footerCols.eq(colIndex),
23549                    nextFooterCol = footerCol.next();
23550
23551                    footerCol.width(newWidth);
23552                    nextFooterCol.width(nextColumnWidth);
23553                }
23554            }
23555        }
23556    },
23557
23558    reclone: function() {
23559        this.clone.remove();
23560        this.clone = this.thead.clone(false);
23561        this.jq.children('table').append(this.clone);
23562    },
23563
23564    switchToRowEdit: function(row) {
23565        this.showRowEditors(row);
23566
23567        if(this.hasBehavior('rowEditInit')) {
23568            var rowIndex = row.data('rk');
23569
23570            var ext = {
23571                params: [{name: this.id + '_rowEditIndex', value: rowIndex}]
23572            };
23573
23574            this.callBehavior('rowEditInit', ext);
23575        }
23576    },
23577
23578    showRowEditors: function(row) {
23579        row.addClass('ui-state-highlight ui-row-editing').children('td.ui-editable-column').each(function() {
23580            var column = $(this);
23581
23582            column.find('.ui-cell-editor-output').hide();
23583            column.find('.ui-cell-editor-input').show();
23584        });
23585    },
23586
23587    /**
23588     * Saves the edited row
23589     */
23590    saveRowEdit: function(rowEditor) {
23591        this.doRowEditRequest(rowEditor, 'save');
23592    },
23593
23594    /**
23595     * Cancels row editing
23596     */
23597    cancelRowEdit: function(rowEditor) {
23598        this.doRowEditRequest(rowEditor, 'cancel');
23599    },
23600
23601    /**
23602     * Sends an ajax request to handle row save or cancel
23603     */
23604    doRowEditRequest: function(rowEditor, action) {
23605        var row = rowEditor.closest('tr'),
23606        rowIndex = row.data('rk'),
23607        expanded = row.hasClass('ui-expanded-row'),
23608        $this = this,
23609        options = {
23610            source: this.id,
23611            process: this.id,
23612            update: this.id,
23613            formId: this.cfg.formId,
23614            params: [{name: this.id + '_rowEditIndex', value: rowIndex},
23615                     {name: this.id + '_rowEditAction', value: action}],
23616            onsuccess: function(responseXML, status, xhr) {
23617                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
23618                        widget: $this,
23619                        handle: function(content) {
23620                            if(expanded) {
23621                                this.collapseRow(row);
23622                            }
23623
23624                            this.updateRows(row, content);
23625                        }
23626                    });
23627
23628                return true;
23629            },
23630            oncomplete: function(xhr, status, args, data) {
23631                if(args && args.validationFailed) {
23632                    $this.invalidateRow(rowIndex);
23633                }
23634            }
23635        };
23636
23637        if(action === 'save') {
23638            this.getRowEditors(row).each(function() {
23639                options.params.push({name: this.id, value: this.id});
23640            });
23641        }
23642
23643        if(action === 'save' && this.hasBehavior('rowEdit')) {
23644            this.callBehavior('rowEdit', options);
23645        }
23646        else if(action === 'cancel' && this.hasBehavior('rowEditCancel')) {
23647            this.callBehavior('rowEditCancel', options);
23648        }
23649        else {
23650            PrimeFaces.ajax.Request.handle(options);
23651        }
23652    },
23653
23654    /*
23655     * Updates rows with given content
23656     */
23657    updateRows: function(row, content) {
23658        this.tbody.children('tr').filter('[data-prk^="'+ row.data('rk') +'"]').remove();
23659        row.replaceWith(content);
23660    },
23661
23662    /**
23663     * Displays row editors in invalid format
23664     */
23665    invalidateRow: function(index) {
23666        this.tbody.children('tr').eq(index).addClass('ui-widget-content ui-row-editing ui-state-error');
23667    },
23668
23669    /**
23670     * Finds all editors of a row
23671     */
23672    getRowEditors: function(row) {
23673        return row.find('div.ui-cell-editor');
23674    },
23675
23676    collapseRow: function(row) {
23677        row.removeClass('ui-expanded-row').next('.ui-expanded-row-content').remove();
23678    },
23679
23680    showCellEditor: function(c) {
23681        this.incellClick = true;
23682
23683        var cell = null;
23684
23685        if(c) {
23686            cell = c;
23687
23688            //remove contextmenu selection highlight
23689            if(this.contextMenuCell) {
23690                this.contextMenuCell.parent().removeClass('ui-state-highlight');
23691            }
23692        }
23693        else {
23694            cell = this.contextMenuCell;
23695        }
23696
23697        var editorInput = cell.find('> .ui-cell-editor > .ui-cell-editor-input');
23698        if(editorInput.length !== 0 && editorInput.children().length === 0 && this.cfg.editMode === 'cell') {
23699            // for lazy cellEditMode
23700            this.cellEditInit(cell);
23701        }
23702        else {
23703            this.showCurrentCell(cell);
23704
23705            if(this.hasBehavior('cellEditInit')) {
23706                var cellInfo = this.getCellMeta(cell);
23707                var ext = {
23708                    params: [{name: this.id + '_cellInfo', value: cellInfo}]
23709                };
23710                this.callBehavior('cellEditInit', ext);
23711            }
23712        }
23713    },
23714
23715    showCurrentCell: function(cell) {
23716        var $this = this;
23717
23718        if(this.currentCell) {
23719            $this.saveCell(this.currentCell);
23720        }
23721
23722        this.currentCell = cell;
23723
23724        var cellEditor = cell.children('div.ui-cell-editor'),
23725        displayContainer = cellEditor.children('div.ui-cell-editor-output'),
23726        inputContainer = cellEditor.children('div.ui-cell-editor-input'),
23727        inputs = inputContainer.find(':input:enabled'),
23728        multi = inputs.length > 1;
23729
23730        cell.addClass('ui-state-highlight ui-cell-editing');
23731        displayContainer.hide();
23732        inputContainer.show();
23733        inputs.eq(0).focus().select();
23734
23735        //metadata
23736        if(multi) {
23737            var oldValues = [];
23738            for(var i = 0; i < inputs.length; i++) {
23739                oldValues.push(inputs.eq(i).val());
23740            }
23741
23742            cell.data('multi-edit', true);
23743            cell.data('old-value', oldValues);
23744        }
23745        else {
23746            cell.data('multi-edit', false);
23747            cell.data('old-value', inputs.eq(0).val());
23748        }
23749
23750        //bind events on demand
23751        if(!cell.data('edit-events-bound')) {
23752            cell.data('edit-events-bound', true);
23753
23754            inputs.on('keydown.treetable-cell', function(e) {
23755                    var keyCode = $.ui.keyCode,
23756                    shiftKey = e.shiftKey,
23757                    key = e.which,
23758                    input = $(this);
23759
23760                    if(key === keyCode.ENTER) {
23761                        $this.saveCell(cell);
23762
23763                        e.preventDefault();
23764                    }
23765                    else if(key === keyCode.TAB) {
23766                        if(multi) {
23767                            var focusIndex = shiftKey ? input.index() - 1 : input.index() + 1;
23768
23769                            if(focusIndex < 0 || (focusIndex === inputs.length)) {
23770                                $this.tabCell(cell, !shiftKey);
23771                            } else {
23772                                inputs.eq(focusIndex).focus();
23773                            }
23774                        }
23775                        else {
23776                            $this.tabCell(cell, !shiftKey);
23777                        }
23778
23779                        e.preventDefault();
23780                    }
23781                    else if(key === keyCode.ESCAPE) {
23782                        $this.doCellEditCancelRequest(cell);
23783                        e.preventDefault();
23784                    }
23785                })
23786                .on('focus.treetable-cell click.treetable-cell', function(e) {
23787                    $this.currentCell = cell;
23788                });
23789        }
23790    },
23791
23792    tabCell: function(cell, forward) {
23793        var targetCell = forward ? cell.nextAll('td.ui-editable-column:first') : cell.prevAll('td.ui-editable-column:first');
23794        if(targetCell.length == 0) {
23795            var tabRow = forward ? cell.parent().next() : cell.parent().prev();
23796            targetCell = forward ? tabRow.children('td.ui-editable-column:first') : tabRow.children('td.ui-editable-column:last');
23797        }
23798
23799        this.showCellEditor(targetCell);
23800    },
23801
23802    saveCell: function(cell) {
23803        var inputs = cell.find('div.ui-cell-editor-input :input:enabled'),
23804        changed = false,
23805        $this = this;
23806
23807        if(cell.data('multi-edit')) {
23808            var oldValues = cell.data('old-value');
23809            for(var i = 0; i < inputs.length; i++) {
23810                if(inputs.eq(i).val() != oldValues[i]) {
23811                    changed = true;
23812                    break;
23813                }
23814            }
23815        }
23816        else {
23817            changed = (inputs.eq(0).val() != cell.data('old-value'));
23818        }
23819
23820        if(changed)
23821            $this.doCellEditRequest(cell);
23822        else
23823            $this.viewMode(cell);
23824
23825        this.currentCell = null;
23826    },
23827
23828    viewMode: function(cell) {
23829        var cellEditor = cell.children('div.ui-cell-editor'),
23830        editableContainer = cellEditor.children('div.ui-cell-editor-input'),
23831        displayContainer = cellEditor.children('div.ui-cell-editor-output');
23832
23833        cell.removeClass('ui-cell-editing ui-state-error ui-state-highlight');
23834        displayContainer.show();
23835        editableContainer.hide();
23836        cell.removeData('old-value').removeData('multi-edit');
23837
23838        if(this.cfg.cellEditMode === "lazy") {
23839            editableContainer.children().remove();
23840        }
23841    },
23842
23843    doCellEditRequest: function(cell) {
23844        var cellEditor = cell.children('.ui-cell-editor'),
23845        cellEditorId = cellEditor.attr('id'),
23846        cellInfo = this.getCellMeta(cell),
23847        $this = this;
23848
23849        var options = {
23850            source: this.id,
23851            process: this.id,
23852            update: this.id,
23853            params: [{name: this.id + '_cellInfo', value: cellInfo},
23854                     {name: cellEditorId, value: cellEditorId}],
23855            onsuccess: function(responseXML, status, xhr) {
23856                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
23857                        widget: $this,
23858                        handle: function(content) {
23859                            cellEditor.children('.ui-cell-editor-output').html(content);
23860                        }
23861                    });
23862
23863                return true;
23864            },
23865            oncomplete: function(xhr, status, args, data) {
23866                if(args.validationFailed)
23867                    cell.addClass('ui-state-error');
23868                else
23869                    $this.viewMode(cell);
23870            }
23871        };
23872
23873        if(this.hasBehavior('cellEdit')) {
23874            this.callBehavior('cellEdit', options);
23875        }
23876        else {
23877            PrimeFaces.ajax.Request.handle(options);
23878        }
23879    },
23880
23881    doCellEditCancelRequest: function(cell) {
23882        var cellEditor = cell.children('.ui-cell-editor'),
23883        cellIndex = cell.index(),
23884        cellInfo = cell.closest('tr').data('rk') + ',' + cellIndex,
23885        $this = this;
23886
23887        this.currentCell = null;
23888
23889        var options = {
23890            source: this.id,
23891            process: this.id,
23892            update: this.id,
23893            params: [{name: this.id + '_cellEditCancel', value: true},
23894                     {name: this.id + '_cellInfo', value: cellInfo}],
23895            onsuccess: function(responseXML, status, xhr) {
23896                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
23897                        widget: $this,
23898                        handle: function(content) {
23899                            cellEditor.children('.ui-cell-editor-input').html(content);
23900                        }
23901                    });
23902
23903                return true;
23904            },
23905            oncomplete: function(xhr, status, args, data) {
23906                $this.viewMode(cell);
23907                cell.data('edit-events-bound', false);
23908            }
23909        };
23910
23911        if(this.hasBehavior('cellEditCancel')) {
23912            this.callBehavior('cellEditCancel', options);
23913        }
23914        else {
23915            PrimeFaces.ajax.Request.handle(options);
23916        }
23917    },
23918
23919    cellEditInit: function(cell) {
23920        var cellEditor = cell.children('.ui-cell-editor'),
23921        cellInfo = this.getCellMeta(cell),
23922        $this = this;
23923
23924        var options = {
23925            source: this.id,
23926            process: this.id,
23927            update: this.id,
23928            global: false,
23929            params: [{name: this.id + '_cellEditInit', value: true},
23930                     {name: this.id + '_cellInfo', value: cellInfo}],
23931            onsuccess: function(responseXML, status, xhr) {
23932                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
23933                        widget: $this,
23934                        handle: function(content) {
23935                            cellEditor.children('.ui-cell-editor-input').html(content);
23936                        }
23937                    });
23938
23939                return true;
23940            },
23941            oncomplete: function(xhr, status, args, data) {
23942                cell.data('edit-events-bound', false);
23943                $this.showCurrentCell(cell);
23944            }
23945        };
23946
23947        if(this.hasBehavior('cellEditInit')) {
23948            this.callBehavior('cellEditInit', options);
23949        }
23950        else {
23951            PrimeFaces.ajax.Request.handle(options);
23952        }
23953    },
23954
23955    getCellMeta: function(cell) {
23956        var cellIndex = cell.index(),
23957            cellInfo = cell.closest('tr').data('rk') + ',' + cellIndex;
23958        return cellInfo;
23959    },
23960
23961    updateVerticalScroll: function() {
23962        if(this.cfg.scrollable && this.cfg.scrollHeight) {
23963            if(this.bodyTable.outerHeight() < this.scrollBody.outerHeight()) {
23964                this.scrollHeaderBox.css('margin-right', 0);
23965                this.scrollFooterBox.css('margin-right', 0);
23966            }
23967            else {
23968                this.scrollHeaderBox.css('margin-right', this.marginRight);
23969                this.scrollFooterBox.css('margin-right', this.marginRight);
23970            }
23971        }
23972    },
23973
23974    shouldSort: function(event, column) {
23975        if(this.isEmpty()) {
23976            return false;
23977        }
23978
23979        var target = $(event.target);
23980        if(target.closest('.ui-column-customfilter', column).length) {
23981            return false;
23982        }
23983
23984        return target.is('th,span');
23985    },
23986
23987    /**
23988     * Returns if there is any data displayed
23989     */
23990    isEmpty: function() {
23991        return this.tbody.children('tr.ui-treetable-empty-message').length === 1;
23992    }
23993});
23994/**
23995 * PrimeFaces Wizard Component
23996 */
23997PrimeFaces.widget.Wizard = PrimeFaces.widget.BaseWidget.extend({
23998
23999    init: function(cfg) {
24000        this._super(cfg);
24001
24002        this.content = $(this.jqId + '_content');
24003        this.backNav = $(this.jqId + '_back');
24004        this.nextNav = $(this.jqId + '_next');
24005        this.cfg.formId = this.jq.parents('form:first').attr('id');
24006        this.currentStep = this.cfg.initialStep;
24007
24008        var _self = this;
24009
24010        //Step controls
24011        if(this.cfg.showStepStatus) {
24012            this.stepControls = $(this.jqId + ' .ui-wizard-step-titles li.ui-wizard-step-title');
24013        }
24014
24015        //Navigation controls
24016        if(this.cfg.showNavBar) {
24017            var currentStepIndex = this.getStepIndex(this.currentStep);
24018
24019            //visuals
24020            PrimeFaces.skinButton(this.backNav);
24021            PrimeFaces.skinButton(this.nextNav);
24022
24023            //events
24024            this.backNav.click(function() {_self.back();});
24025            this.nextNav.click(function() {_self.next();});
24026
24027            if(currentStepIndex == 0)
24028                this.backNav.hide();
24029            else if(currentStepIndex == this.cfg.steps.length - 1)
24030                this.nextNav.hide();
24031        }
24032    },
24033
24034    back: function() {
24035        if(this.cfg.onback) {
24036            var value = this.cfg.onback.call(this);
24037            if(value === false) {
24038                return;
24039            }
24040        }
24041
24042        var targetStepIndex = this.getStepIndex(this.currentStep) - 1;
24043        if(targetStepIndex >= 0) {
24044            var stepToGo = this.cfg.steps[targetStepIndex];
24045            this.loadStep(stepToGo, "back");
24046        }
24047    },
24048
24049    next: function() {
24050        if(this.cfg.onnext) {
24051            var value = this.cfg.onnext.call(this);
24052            if(value === false) {
24053                return;
24054            }
24055        }
24056
24057        var targetStepIndex = this.getStepIndex(this.currentStep) + 1;
24058        if(targetStepIndex < this.cfg.steps.length) {
24059            var stepToGo = this.cfg.steps[targetStepIndex];
24060            this.loadStep(stepToGo, "next");
24061        }
24062    },
24063
24064    loadStep: function(stepToGo, event) {
24065        var $this = this,
24066        options = {
24067            source: this.id,
24068            process: this.id,
24069            update: this.id,
24070            formId: this.cfg.formId,
24071            params: [
24072                {name: this.id + '_direction', value: event},
24073                {name: this.id + '_stepToGo', value: stepToGo}
24074            ],
24075            onsuccess: function(responseXML, status, xhr) {
24076                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
24077                        widget: $this,
24078                        handle: function(content) {
24079                            this.content.html(content);
24080                        }
24081                    });
24082
24083                return true;
24084            },
24085            oncomplete: function(xhr, status, args, data) {
24086                $this.currentStep = args.currentStep;
24087
24088                if(!args.validationFailed) {
24089                    var currentStepIndex = $this.getStepIndex($this.currentStep);
24090
24091                    if($this.cfg.showNavBar) {
24092                        if(currentStepIndex === $this.cfg.steps.length - 1) {
24093                            $this.hideNextNav();
24094                            $this.showBackNav();
24095                        } else if(currentStepIndex === 0) {
24096                            $this.hideBackNav();
24097                            $this.showNextNav();
24098                        } else {
24099                            $this.showBackNav();
24100                            $this.showNextNav();
24101                        }
24102                    }
24103
24104                    //update step status
24105                    if($this.cfg.showStepStatus) {
24106                        $this.stepControls.removeClass('ui-state-highlight');
24107                        $($this.stepControls.get(currentStepIndex)).addClass('ui-state-highlight');
24108                    }
24109                }
24110            }
24111        };
24112
24113        if(this.hasBehavior(event)) {
24114            this.callBehavior(event, options);
24115        }
24116        else {
24117            PrimeFaces.ajax.Request.handle(options);
24118        }
24119    },
24120
24121    getStepIndex: function(step) {
24122        for(var i=0; i < this.cfg.steps.length; i++) {
24123            if(this.cfg.steps[i] == step)
24124                return i;
24125        }
24126
24127        return -1;
24128    },
24129
24130    showNextNav: function() {
24131        this.nextNav.fadeIn();
24132    },
24133
24134    hideNextNav: function() {
24135        this.nextNav.fadeOut();
24136    },
24137
24138    showBackNav: function() {
24139        this.backNav.fadeIn();
24140    },
24141
24142    hideBackNav: function() {
24143        this.backNav.fadeOut();
24144    }
24145
24146});
24147
24148
24149PrimeFaces.widget.TriStateCheckbox = PrimeFaces.widget.BaseWidget.extend({
24150
24151    init:function (cfg) {
24152        this._super(cfg);
24153
24154        this.input = $(this.jqId + '_input');
24155        this.box = this.jq.find('.ui-chkbox-box');
24156        this.icon = this.box.children('.ui-chkbox-icon');
24157        this.itemLabel = this.jq.find('.ui-chkbox-label');
24158        this.disabled = this.input.is(':disabled');
24159        this.fixedMod = function(number, mod){
24160            return ((number % mod) + mod) % mod;
24161        };
24162
24163        var $this = this;
24164
24165        //bind events if not disabled
24166        if (!this.disabled) {
24167            this.box.on('mouseover.triStateCheckbox', function () {
24168                $this.box.addClass('ui-state-hover');
24169            })
24170            .on('mouseout.triStateCheckbox', function () {
24171                $this.box.removeClass('ui-state-hover');
24172            })
24173            .on('click.triStateCheckbox', function () {
24174                $this.toggle(1);
24175                $this.input.trigger('focus');
24176            });
24177            
24178            this.input.on('focus.triStateCheckbox', function () {
24179                $this.box.addClass('ui-state-focus');
24180            })
24181            .on('blur.triStateCheckbox', function () {
24182                $this.box.removeClass('ui-state-focus');
24183            })
24184            .on('keydown.triStateCheckbox', function (e) {
24185                var keyCode = $.ui.keyCode;
24186
24187                switch (e.which) {
24188                    case keyCode.SPACE:
24189                    case keyCode.UP:
24190                    case keyCode.RIGHT:
24191                    case keyCode.LEFT:
24192                    case keyCode.DOWN:
24193                        e.preventDefault();
24194                        break;
24195                }
24196            })            
24197            .on('keyup.triStateCheckbox', function (e) {
24198                var keyCode = $.ui.keyCode;
24199
24200                switch(e.which) {
24201                    case keyCode.SPACE:
24202                    case keyCode.UP:
24203                    case keyCode.RIGHT:
24204                        $this.toggle(1);
24205                        break;
24206                    case keyCode.LEFT:
24207                    case keyCode.DOWN:
24208                        $this.toggle(-1);
24209                        break;
24210                }
24211            });
24212
24213            //toggle state on label click
24214            this.itemLabel.on('click.triStateCheckbox', function() {
24215                $this.toggle(1);
24216                $this.input.trigger('focus');
24217            });
24218
24219            // client behaviors
24220            if (this.cfg.behaviors) {
24221                PrimeFaces.attachBehaviors(this.input, this.cfg.behaviors);
24222            }
24223        }
24224
24225        // pfs metadata
24226        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
24227    },
24228
24229    toggle:function (direction) {
24230        if (!this.disabled) {
24231            // default to switch to next state
24232            if (isNaN(direction)) {
24233                direction = 1;
24234            }
24235
24236            var oldValue = parseInt(this.input.val());
24237            var newValue = this.fixedMod((oldValue + direction), 3);
24238            this.input.val(newValue);
24239
24240            // remove / add def. icon and active classes
24241            if (newValue == 0) {
24242                this.box.removeClass('ui-state-active');
24243            } else {
24244                this.box.addClass('ui-state-active');
24245            }
24246
24247            // remove old icon and add the new one
24248            var iconsClasses = this.box.data('iconstates');
24249            this.icon.removeClass(iconsClasses[oldValue]).addClass(iconsClasses[newValue]);
24250
24251            // change title to the new one
24252            var iconTitles = this.box.data('titlestates');
24253            if (iconTitles != null && iconTitles.titles != null && iconTitles.titles.length > 0) {
24254                this.box.attr('title', iconTitles.titles[newValue]);
24255            }
24256
24257            // fire change event
24258            this.input.change();
24259        }
24260    }
24261});
24262
24263
24264/**
24265 * PrimeFaces Chips Widget
24266 */
24267PrimeFaces.widget.Chips = PrimeFaces.widget.BaseWidget.extend({
24268
24269    init: function(cfg) {
24270        this._super(cfg);
24271
24272        this.input = $(this.jqId + '_input');
24273        this.hinput = $(this.jqId + '_hinput');
24274        this.itemContainer = this.jq.children('ul');
24275        this.inputContainer = this.itemContainer.children('.ui-chips-input-token');
24276
24277        //pfs metadata
24278        this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
24279        this.hinput.data(PrimeFaces.CLIENT_ID_DATA, this.id);
24280        this.placeholder = this.input.attr('placeholder');
24281
24282        this.bindEvents();
24283    },
24284
24285    bindEvents: function() {
24286        var $this = this;
24287
24288        this.itemContainer.hover(function() {
24289                $(this).addClass('ui-state-hover');
24290            },
24291            function() {
24292                $(this).removeClass('ui-state-hover');
24293            }
24294        ).click(function() {
24295            $this.input.focus();
24296        });
24297
24298
24299        this.input.on('focus.chips', function() {
24300            $this.itemContainer.addClass('ui-state-focus');
24301        }).on('blur.chips', function() {
24302            $this.itemContainer.removeClass('ui-state-focus');
24303            
24304            if ($this.cfg.addOnBlur) {
24305                $this.addItem($(this).val(), false);
24306            }
24307        }).on('keydown.chips', function(e) {
24308            var value = $(this).val();
24309
24310            switch(e.which) {
24311                //backspace
24312                case 8:
24313                    if(value.length === 0 && $this.hinput.children('option') && $this.hinput.children('option').length > 0) {
24314                        var lastOption = $this.hinput.children('option:last'),
24315                        index = lastOption.index();
24316                        $this.removeItem($($this.itemContainer.children('li.ui-chips-token').get(index)));
24317                    }
24318                break;
24319
24320                //enter
24321                case 13:
24322                    $this.addItem(value, true);
24323                    e.preventDefault();
24324                break;
24325
24326                default:
24327                    if($this.cfg.max && $this.cfg.max === $this.hinput.children('option').length) {
24328                        e.preventDefault();
24329                    }
24330                break;
24331            }
24332        });
24333
24334        var closeSelector = '> li.ui-chips-token > .ui-chips-token-icon';
24335        this.itemContainer.off('click', closeSelector).on('click', closeSelector, null, function(event) {
24336            $this.removeItem($(this).parent());
24337        });
24338    },
24339
24340    addItem : function(value, refocus) {
24341        if(value && value.trim().length && (!this.cfg.max||this.cfg.max > this.hinput.children('option').length)) {
24342            var escapedValue = PrimeFaces.escapeHTML(value);
24343            var itemDisplayMarkup = '<li class="ui-chips-token ui-state-active ui-corner-all">';
24344            itemDisplayMarkup += '<span class="ui-chips-token-icon ui-icon ui-icon-close"></span>';
24345            itemDisplayMarkup += '<span class="ui-chips-token-label">' + escapedValue + '</span></li>';
24346
24347            this.inputContainer.before(itemDisplayMarkup);
24348            this.input.val('');
24349            this.input.removeAttr('placeholder');
24350            
24351            if (refocus) {
24352                this.input.focus();
24353            }
24354
24355            this.hinput.append('<option value="' + escapedValue + '" selected="selected"></option>');
24356            this.invokeItemSelectBehavior(escapedValue);
24357        }
24358    },
24359
24360    removeItem: function(item) {
24361        var itemIndex = this.itemContainer.children('li.ui-chips-token').index(item);
24362        var itemValue = item.find('span.ui-chips-token-label').html()
24363        $this = this;
24364
24365        //remove from options
24366        this.hinput.children('option').eq(itemIndex).remove();
24367
24368        item.fadeOut('fast', function() {
24369            var token = $(this);
24370
24371            token.remove();
24372
24373            $this.invokeItemUnselectBehavior(itemValue);
24374        });
24375        
24376        // if empty return placeholder
24377        if (this.placeholder && this.hinput.children('option').length === 0) {
24378            this.input.attr('placeholder', this.placeholder);
24379        }
24380    },
24381
24382    invokeItemSelectBehavior: function(itemValue) {
24383        if(this.hasBehavior('itemSelect')) {
24384            var ext = {
24385                params : [
24386                    {name: this.id + '_itemSelect', value: itemValue}
24387                ]
24388            };
24389
24390            this.callBehavior('itemSelect', ext);
24391        }
24392    },
24393
24394    invokeItemUnselectBehavior: function(itemValue) {
24395        if(this.hasBehavior('itemUnselect')) {
24396            var ext = {
24397                params : [
24398                    {name: this.id + '_itemUnselect', value: itemValue}
24399                ]
24400            };
24401
24402            this.callBehavior('itemUnselect', ext);
24403        }
24404    }
24405});
24406/**
24407 * PrimeFaces Sidebar Widget
24408 */
24409PrimeFaces.widget.Sidebar = PrimeFaces.widget.DynamicOverlayWidget.extend({
24410
24411    init: function(cfg) {
24412        this._super(cfg);
24413
24414        this.closeIcon = this.jq.children('.ui-sidebar-close');
24415        this.cfg.baseZIndex = this.cfg.baseZIndex||0;
24416
24417        //aria
24418        this.applyARIA();
24419
24420        if(this.cfg.visible){
24421            this.show();
24422        }
24423
24424        this.bindEvents();
24425    },
24426
24427    bindEvents: function() {
24428        var $this = this;
24429
24430        this.closeIcon.on('mouseover', function() {
24431            $(this).addClass('ui-state-hover');
24432        }).on('mouseout', function() {
24433            $(this).removeClass('ui-state-hover');
24434        }).on('focus', function() {
24435            $(this).addClass('ui-state-focus');
24436        }).on('blur', function() {
24437            $(this).removeClass('ui-state-focus');
24438        }).on('click', function(e) {
24439            $this.hide();
24440            e.preventDefault();
24441        });
24442    },
24443
24444    show: function() {
24445        if(this.isVisible()) {
24446            return;
24447        }
24448
24449        this.jq.addClass('ui-sidebar-active');
24450        this.jq.css('z-index', this.cfg.baseZIndex + (++PrimeFaces.zindex));
24451
24452        this.postShow();
24453        this.enableModality();
24454    },
24455
24456    postShow: function() {
24457        PrimeFaces.invokeDeferredRenders(this.id);
24458
24459        //execute user defined callback
24460        if(this.cfg.onShow) {
24461            this.cfg.onShow.call(this);
24462        }
24463
24464        this.jq.attr({
24465            'aria-hidden': false
24466            ,'aria-live': 'polite'
24467        });
24468    },
24469
24470    hide: function() {
24471        if(!this.isVisible()) {
24472            return;
24473        }
24474
24475        this.jq.removeClass('ui-sidebar-active');
24476        this.onHide();
24477        this.disableModality();
24478    },
24479
24480    isVisible: function() {
24481        return this.jq.hasClass('ui-sidebar-active');
24482    },
24483
24484    onHide: function(event, ui) {
24485        this.jq.attr({
24486            'aria-hidden': true
24487            ,'aria-live': 'off'
24488        });
24489
24490        if(this.cfg.onHide) {
24491            this.cfg.onHide.call(this, event, ui);
24492        }
24493    },
24494
24495    toggle: function() {
24496        if(this.isVisible())
24497            this.hide();
24498        else
24499            this.show();
24500    },
24501
24502    //@override
24503    enableModality: function() {
24504        this._super();
24505
24506        var $this = this;
24507        this.modalOverlay.on('click', function() {
24508            $this.hide();
24509        });
24510    },
24511
24512    //@override
24513    getModalTabbables: function(){
24514        return this.jq.find(':tabbable');
24515    },
24516
24517    applyARIA: function() {
24518        this.jq.attr({
24519            'role': 'dialog'
24520            ,'aria-hidden': !this.cfg.visible
24521        });
24522
24523        this.closeIcon.attr('role', 'button');
24524    }
24525
24526});
24527/*
24528 * PrimeFaces DataView Widget
24529 */
24530PrimeFaces.widget.DataView = PrimeFaces.widget.BaseWidget.extend({
24531
24532    init: function(cfg) {
24533        this._super(cfg);
24534
24535        this.header = this.jq.children('.ui-dataview-header');
24536        this.content = this.jq.children('.ui-dataview-content');
24537        this.layoutOptions = this.header.children('.ui-dataview-layout-options');
24538        this.buttons = this.layoutOptions.children('div');
24539        this.cfg.formId = $(this.jqId).closest('form').attr('id');
24540
24541        if(this.cfg.paginator) {
24542            this.setupPaginator();
24543        }
24544
24545        this.bindEvents();
24546    },
24547
24548    setupPaginator: function() {
24549        var $this = this;
24550        this.cfg.paginator.paginate = function(newState) {
24551            $this.handlePagination(newState);
24552        };
24553
24554        this.paginator = new PrimeFaces.widget.Paginator(this.cfg.paginator);
24555    },
24556
24557    bindEvents: function () {
24558        var $this = this;
24559
24560        this.buttons.on('mouseover', function() {
24561            var button = $(this);
24562            button.addClass('ui-state-hover');
24563        })
24564        .on('mouseout', function() {
24565            $(this).removeClass('ui-state-hover');
24566        })
24567        .on('click', function() {
24568            var button = $(this),
24569            radio = button.children(':radio');
24570
24571            if(!radio.prop('checked')) {
24572                $this.select(button);
24573            }
24574        });
24575
24576        /* For keyboard accessibility */
24577        this.buttons.on('focus.dataview-button', function(){
24578            var button = $(this);
24579            button.addClass('ui-state-focus');
24580        })
24581        .on('blur.dataview-button', function(){
24582            var button = $(this);
24583            button.removeClass('ui-state-focus');
24584        })
24585        .on('keydown.dataview-button', function(e) {
24586            var keyCode = $.ui.keyCode,
24587            key = e.which;
24588
24589            if(key === keyCode.SPACE || key === keyCode.ENTER) {
24590                var button = $(this),
24591                radio = button.children(':radio');
24592
24593                if(!radio.prop('checked')) {
24594                    $this.select(button);
24595                }
24596                e.preventDefault();
24597            }
24598        });
24599    },
24600
24601    select: function(button) {
24602        this.buttons.filter('.ui-state-active').removeClass('ui-state-active ui-state-hover').children(':radio').prop('checked', false);
24603
24604        button.addClass('ui-state-active').children(':radio').prop('checked', true);
24605
24606        this.loadLayoutContent(button.children(':radio').val());
24607    },
24608
24609    loadLayoutContent: function(layout) {
24610        var $this = this,
24611        options = {
24612            source: this.id,
24613            process: this.id,
24614            update: this.id,
24615            params: [
24616                {name: this.id + '_layout', value: layout}
24617            ],
24618            onsuccess: function(responseXML, status, xhr) {
24619                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
24620                        widget: $this,
24621                        handle: function(content) {
24622                            this.content.html(content);
24623                        }
24624                    });
24625
24626                return true;
24627            },
24628            oncomplete: function() {
24629                $this.jq.removeClass('ui-dataview-grid ui-dataview-list').addClass('ui-dataview-' + layout);
24630            }
24631        };
24632
24633        PrimeFaces.ajax.Request.handle(options);
24634    },
24635
24636    handlePagination: function(newState) {
24637        var $this = this,
24638        options = {
24639            source: this.id,
24640            update: this.id,
24641            process: this.id,
24642            formId: this.cfg.formId,
24643            params: [
24644                {name: this.id + '_pagination', value: true},
24645                {name: this.id + '_first', value: newState.first},
24646                {name: this.id + '_rows', value: newState.rows}
24647            ],
24648            onsuccess: function(responseXML, status, xhr) {
24649                PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
24650                        widget: $this,
24651                        handle: function(content) {
24652                            this.content.html(content);
24653                        }
24654                    });
24655
24656                return true;
24657            },
24658            oncomplete: function() {
24659                $this.paginator.cfg.page = newState.page;
24660                $this.paginator.updateUI();
24661            }
24662        };
24663
24664        if(this.hasBehavior('page')) {
24665            this.callBehavior('page', options);
24666        }
24667        else {
24668            PrimeFaces.ajax.Request.handle(options);
24669        }
24670    },
24671
24672    getPaginator: function() {
24673        return this.paginator;
24674    }
24675});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.