Project

General

Profile

Download (87.3 KB) Statistics
| Branch: | Revision:

git_sitools_idoc / solar / project_modules_sitools2v3 / Module_DatasetExplorerOchart_sitools2v3 / OChart.js @ master

1

    
2
Ext.namespace('sitools.user.view.modules.datasetExplorerOchart');
3
Ext.define('sitools.user.view.modules.datasetExplorerOchart.OChart',{
4
    extend: 'Ext.Component',
5
    alias: 'widget.ochart',
6
    itemId: 'ochart',
7
    requires:[
8
        'Ext.LoadMask',
9
        'Ext.data.StoreManager',
10
        'Ext.dom.Query',
11
        'sitools.user.view.modules.datasetExplorerOchart.OChartModel',
12
        'Ext.dd.ScrollManager'
13
    ],
14

    
15
    mixins:{
16
        bindable: 'Ext.util.Bindable'
17
    },
18

    
19
    baseCls: Ext.baseCSSPrefix + 'ochart',
20

    
21
    /**
22
     * In some places it's need to render full tables because <IE9 have some bugs and makes tr and table readonly
23
     * @private
24
     */
25
    renderBuffer: document.createElement('div'),
26

    
27
    /**
28
     * @cfg {Boolean} rootVisible=true true to include the root node in the chart.
29
     */
30
    rootVisible: true,
31

    
32
    /**
33
     * @cfg {Boolean} toolsVisible=true true to show the item floating tools.
34
     */
35
    toolsVisible: true,
36

    
37
    /**
38
     * @cfg {Ext.data.NodeInterface} root=null The chart´s root node or null for the store root node.
39
     */
40
    root: null,
41

    
42
    /**
43
     * @cfg {Boolean} autoLoadStore=tru
44
     * If this config is true and the store isn't loaded or already loading, the component will trigger a load command to the store
45
     * during component initialization.
46
     */
47
    autoLoadStore: true,
48

    
49
    /**
50
     * @cfg {String} displayField="text" The field used to render the node contents.
51
     */
52
    displayField: 'text',
53

    
54
    /**
55
     * @cfg {Boolean/Object} loadMask
56
     * False to disable a load mask from displaying while the view is loading. This can also be a
57
     * {@link Ext.LoadMask} configuration object.
58
     */
59
    loadMask: true,
60

    
61
    /**
62
     * @cfg {String} loadingText
63
     * A string to display during data load operations.  If specified, this text will be
64
     * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
65
     * contents will continue to display normally until the new data is loaded and the contents are replaced.
66
     */
67
    loadingText: 'Loading...',
68

    
69
    /**
70
     * @cfg {Boolean} loadingUseMsg
71
     * Whether or not to use the loading message.
72
     * @private
73
     */
74
    loadingUseMsg: true,
75

    
76
    /**
77
     * @cfg {Boolean} allowContainerDrop=true
78
     * False to disable dropping itens on the container, true to allow dropping itens on the container.
79
     * When itens are dropped on the container they will be appended to the root node.
80
     */
81
    allowContainerDrop: true,
82

    
83
    /**
84
     * @cfg {String} loadingCls
85
     * The CSS class to apply to the loading message element. Defaults to Ext.LoadMask.prototype.msgCls "x-mask-loading".
86
     */
87

    
88
    /**
89
     * @cfg {Number} lineWeight=1 Weight of node connector lines.
90
     */
91
    lineWeight: 1,
92

    
93
    /**
94
     * @property {String} lineColor="#000" HTML color to use for the node connector lines.
95
     */
96
    lineColor: '#000',
97

    
98
    /**
99
     * @cfg {Number} levelSpacing=15 Space in pixels between the parent and children nodes.
100
     */
101
    levelSpacing: 15,
102

    
103
    /**
104
     * @cfg {Number} nodeSpacing=10 Margin in pixels between adjacent nodes.
105
     */
106
    nodeSpacing: 10,
107

    
108
    /**
109
     * @cfg {String} itemCls=null Additional class for the node content element.
110
     */
111
    itemCls: null,
112

    
113
    /** inheritdoc */
114
    renderTpl: ['<table class="{baseCls}-wrap" cellpadding="0" cellspacing="0" border="0"><tbody><tr class="{baseCls}-container"></tr></tbody></table>'],
115

    
116
    /**
117
     * @cfg {Ext.XTemplate/String/String[]}} downLineTpl Node down line connector template
118
     * @private
119
     */
120
    downLineTpl: [
121
        '<tr class="{view.baseCls}-lines {view.downLineCls}">',
122
            '<td colspan="{node.childNodes.length}">',
123
                '<div class="{view.baseCls}-left" style="border-top-width: {view.lineWeight}px; height: {view.levelSpacing}px; border-color:{view.lineColor} !important;"></div>',
124
                '<div class="{view.baseCls}-right" style="border-top-width: {view.lineWeight}px; border-left-width: {view.lineWeight}px; height: {view.levelSpacing}px; border-color:{view.lineColor} !important;"></div>',
125
            '</td>',
126
        '</tr>',
127
        '<tr class="{view.expanderRowCls}">',
128
            '<td colspan="{node.childNodes.length}">',
129
                '<span class="{view.expanderCmpCls}{[values.view.expanderCls ? " "+values.view.expanderCls : ""]}" data-qtip="{view.expandTip:htmlEncode}"></span>',
130
            '</td>',
131
        '</tr>'
132
    ],
133

    
134
    /**
135
     * @cfg {Ext.XTemplate/String/String[]}} childrenLineTpl Simple child inner line connector template
136
     * @private
137
     */
138
    childrenLineTpl: [
139
        '<div class="{view.baseCls}-left" style="border-top-width:{view.lineWeight}px; height: {view.levelSpacing}px; border-color:{view.lineColor} !important;"></div>',
140
        '<div class="{view.baseCls}-right" style="border-top-width:{view.lineWeight}px; border-left-width:{view.lineWeight}px; height: {view.levelSpacing}px; border-color:{view.lineColor} !important;"></div>'
141
    ],
142

    
143
    /**
144
     * @cfg {Ext.XTemplate/String/String[]}} childrenLinesTpl Multiple child line connector template
145
     * @private
146
     */
147
    childrenLinesTpl: [
148
        '<tpl if="node.childNodes.length &gt; 1">',
149
            //multiple lines
150
            '<tr class="{view.baseCls}-lines {view.childrenLinesCls}">',
151
                '{[this.childLines(values.view, values.node.childNodes)]}',
152
            '</tr>',
153
        '</tpl>',
154
        {
155
            childLines: function(view, nodes){
156
                var out = [],
157
                    len = nodes.length,
158
                    last = len- 1,
159
                    cls = view.baseCls,
160
                    clsLeft = cls + '-left',
161
                    clsRight = cls + '-right',
162
                    lineWeight = view.lineWeight,
163
                    lineColor = view.lineColor,
164
                    height = view.levelSpacing,
165
                    div= '<div class="{0}" style="border-color: {1}; border-top-width: {2}px; border-left-width: {3}px; border-right-width: {4}px; height: {5}px;"></div>',
166
                    format = Ext.String.format,
167
                    i, td;
168

    
169
                for(i = 0; i<len; ++i){
170
                    td = '<td';
171
                    //it's a first or last line?
172
                    if(i == 0) td += ' class="' + cls + '-first"';
173
                    else if(i == last) td += ' class="' + cls + '-last"';
174
                    td += '>';
175

    
176
                    td += format(div, clsLeft, lineColor, lineWeight, 0, i == last ? lineWeight : 0, height);
177
                    td += format(div, clsRight, lineColor, lineWeight, lineWeight, 0, height);
178

    
179
                    td += '</td>';
180

    
181
                    out.push(td);
182
                }
183

    
184
                return out.join('');
185
            }
186
        }
187
    ],
188

    
189
    /**
190
     * @cfg {Ext.XTemplate/String/String[]}} childrenTpl Container with children template
191
     * @private
192
     */
193
    childrenTpl: [
194
        '<tr class="{view.containerCls}">',
195
            '{%values.view.renderNodes(values.node.childNodes, out)%}',
196
        '</tr>'
197
    ],
198

    
199
    /**
200
     * @cfg {Ext.XTemplate/String/String[]}} containerTpl Simple container template
201
     * @private
202
     */
203
    containerTpl: '<tr class="{view.containerCls}"></tr>',
204

    
205
    /**
206
     * Record node image format
207
     * @private
208
     */
209
    imgText: '<img src="{0}" class="{1}" />',
210

    
211
    /**
212
     * Inner node structure template
213
     * @private
214
     */
215
    innerNodeTpl: [
216
        '<table cellpadding="0" cellspacing="0" border="0"><tbody>',
217
            //node content
218
            '<tr class="{view.nodeContentRowCls}">',
219
                '<td colspan="{node.childNodes.length}">',
220
                    '<tpl if="node.data.icon || node.data.iconCls">',
221
                        '{[Ext.String.format(values.view.imgText, values.node.data.icon ? values.node.data.icon : Ext.BLANK_IMAGE_URL, values.node.data.iconCls ? values.node.data.iconCls : "")]}',
222
                    '</tpl>',
223
                    '<span id="{nodeId}" class="{view.nodeContentCls}' +
224
                        '{[values.view.itemCls ? " "+values.view.itemCls : ""]}',
225
                        '{[values.node.get("cls") ? " "+values.node.get("cls") : ""]}',
226
                        '" data-recordId="{[values.view.getRecordId(values.node)]}"',
227
                        '<tpl if="node.data.qtitle && node.data.qtip">',
228
                            ' data-qtitle="{node.data.qtitle:htmlEncode}"',
229
                        '</tpl>',
230
                        '<tpl if="node.data.qtip">',
231
                            ' data-qtip="{node.data.qtip:htmlEncode}"',
232
                        '</tpl>',
233
                        '>{[values.view.renderItem(values.view, values.node)]}',
234
                    '</span>',
235
                '</td>',
236
            '</tr>',
237

    
238
            //children
239
            '<tpl if="this.handleChildren(node)">',
240
                //down line
241
                '{[values.view.downLineTpl.apply(values)]}',
242

    
243
                //children lines
244
                '{[values.view.childrenLinesTpl.apply(values)]}',
245

    
246
                //children container
247
                '{[this.renderChildren(values)]}',
248
            '</tpl>',
249
        '</tbody></table>',
250
        {
251
            renderChildren: function(values){
252
                var out=[];
253
                values.view.childrenTpl.applyOut(values, out);
254
                return out.join('');
255
            },
256
            handleChildren: function(node){
257
                return (node.childNodes.length || (!node.isLeaf() && !node.get('loaded')));
258
            }
259
        }
260
    ],
261

    
262
    /**
263
     * @cfg {Ext.XTemplate/String/String[]}} nodeTpl Full node component template
264
     * @private
265
     */
266
    nodeTpl: [
267
        '<td class="{view.nodeCls}{[!values.node.isLeaf() && !values.node.isExpanded() ? " " + values.view.collapseCls: ""]}" style="padding: 0 {view.nodeSpacing}px;">',
268
        '{[values.view.innerNodeTpl.apply(values)]}',
269
        '</td>'
270
    ],
271

    
272
    /**
273
     * @cfg {Ext.XTemplate/String/String[]} itemTpl Template used to render the node's content.
274
     */
275
    itemTpl: '{text}',
276

    
277
    /**
278
     * @cfg {String} wrapperSelector Component wrapper CSS selector.
279
     * @private
280
     */
281
    wrapperSelector: '.'+Ext.baseCSSPrefix + 'ochart-wrap',
282

    
283
    /**
284
     * @cfg {String} itemSelector Node content CSS selector
285
     * private
286
     */
287
    itemSelector: '.'+Ext.baseCSSPrefix + 'ochart-node-content',
288

    
289
    /**
290
     * @cfg {String} itemRowSelector Item content row CSS selector
291
     * private
292
     */
293
    itemRowSelector: '.'+Ext.baseCSSPrefix + 'ochart-node-row',
294

    
295
    /**
296
     * @cfg {String} nodeItemContainerSelector Node content row CSS selector
297
     * private
298
     */
299
    nodeItemContainerSelector: 'table > tbody > .'+Ext.baseCSSPrefix + 'ochart-node-row',
300

    
301
    /**
302
     * @property {String} nodeSelector Node container CSS selector
303
     * private
304
     */
305
    nodeSelector: '.'+Ext.baseCSSPrefix + 'ochart-node',
306

    
307
    nodeBodySelector: 'table > tbody',
308

    
309
    nodeContainerSelector: 'table > tbody > .'+Ext.baseCSSPrefix + 'ochart-container',
310

    
311
    expanderSelector: '.'+Ext.baseCSSPrefix + 'ochart-expander',
312

    
313
    inlineExpanderContainerSelector: 'table > tbody > .'+Ext.baseCSSPrefix + 'ochart-expander-row',
314

    
315
    inlineExpanderContentSelector: 'table > tbody > .'+Ext.baseCSSPrefix + 'ochart-expander-row > td',
316

    
317
    downLineContainerSelector: 'table > tbody > .'+Ext.baseCSSPrefix + 'ochart-down',
318

    
319
    downLineSelector: 'table > tbody > .'+Ext.baseCSSPrefix + 'ochart-down > td',
320

    
321
    childrenLinesSelector: 'table > tbody > .' + Ext.baseCSSPrefix + 'ochart-children-lines',
322

    
323
    expandTip: 'Click here to expand this node.',
324

    
325
    collapseTip: 'Click here to collapse this node.',
326

    
327
    addBeforeTip: 'Add a new item before this.',
328

    
329
    addAfterTip: 'Add a new item after this.',
330

    
331
    addChildTip: 'Add a new child node to this item.',
332

    
333
    removeItemTip: 'Remove this item.',
334

    
335
    /**
336
     * @cfg {Boolean} trackOver
337
     * When `true` the {@link #overItemCls} will be applied to nodes when hovered over.
338
     * This in return will also cause {#highlightitem} and
339
     * {#unhighlightitem} events to be fired.
340
     *
341
     * Enabled automatically when the {@link #overItemCls} config is set.
342
     */
343
    trackOver: false,
344

    
345
    /**
346
     * @cfg {Number} [mouseOverOutBuffer=20]
347
     * The number of milliseconds to buffer mouseover and mouseout event handling on view items.
348
     *
349
     * Configure this as `false` to process mouseover and mouseout events immediately.
350
     */
351
    mouseOverOutBuffer: 20,
352

    
353
    inputTagRe: /^textarea$|^input$/i,
354

    
355
    /**
356
     * @cfg {String} overItemCls='x-ochart-over-node' Mouse over item class.
357
     */
358
    overItemCls: Ext.baseCSSPrefix + 'ochart-over-node',
359

    
360
    /**
361
     * @cfg {String} expanderCls Expand tool class.
362
     */
363
    expanderCls: null,
364

    
365
    /**
366
     * @cfg {String} addBeforeCls Add before tool class.
367
     */
368
    addBeforeCls: null,
369

    
370
    /**
371
     * @cfg {String} addAfterCls Add after tool class.
372
     */
373
    addAfterCls: null,
374

    
375
    /**
376
     * @cfg {String} addChildCls Add child tool class.
377
     */
378
    addChildCls: null,
379

    
380
    /**
381
     * @cfg {String} removeItemCls Remove item tool class.
382
     */
383
    removeItemCls: null,
384

    
385
    /**
386
     * @cfg {String} selectedItemCls
387
     * A CSS class to apply to each selected item in the view.
388
     */
389
    selectedItemCls: Ext.baseCSSPrefix + 'item-selected',
390

    
391
    /**
392
     * @cfg {String} collapseCls
393
     * A CSS class to apply to each item that is collapsed in the view.
394
     */
395
    collapseCls: Ext.baseCSSPrefix + 'item-collapsed',
396

    
397
    inheritableStatics: {
398
        /**
399
         * Event maps
400
         *
401
         * @static
402
         * @protected
403
         */
404
        EventMap: {
405
            mousedown  : 'MouseDown',
406
            mouseup    : 'MouseUp',
407
            click      : 'Click',
408
            dblclick   : 'DblClick',
409
            contextmenu: 'ContextMenu',
410
            mouseover  : 'MouseOver',
411
            mouseout   : 'MouseOut',
412
            mouseenter : 'MouseEnter',
413
            mouseleave : 'MouseLeave',
414
            keydown    : 'KeyDown',
415
            focus      : 'Focus'
416
        }
417
    },
418

    
419
    /**
420
     * @cfg {String} triggerEvent="itemclick"
421
     * Trigger event used by the selection model to handle item click
422
     *
423
     * @private
424
     */
425
    triggerEvent: 'itemclick',
426

    
427
    /**
428
     * @cfg {String} triggerCtEvent="containerclick"
429
     * Trigger event used by the selection model to handle container click
430
     *
431
     * @private
432
     */
433
    triggerCtEvent: 'containerclick',
434

    
435
    /** @inheritdoc */
436
    initComponent: function(){
437
        var me = this,
438
            store = me.store,
439
            root = me.root;
440

    
441
        /**
442
         * @cfg {String} wrapperCls Component wrapper class
443
         * @private
444
         */
445
        me.wrapperCls = me.baseCls + '-wrap';
446

    
447
        /**
448
         * @cfg {String} containerCls Node container class
449
         * @private
450
         */
451
        me.containerCls = me.baseCls + '-container';
452

    
453
        /**
454
         * @cfg {String} nodeCls Node component class
455
         * @private
456
         */
457
        me.nodeCls = me.baseCls + '-node';
458

    
459
        /**
460
         * @cfg {String} nodeContentRowCls Node's content line class
461
         * @private
462
         */
463
        me.nodeContentRowCls = me.baseCls + '-node-row';
464

    
465
        /**
466
         * @cfg {String} nodeContentCls Node's content class
467
         * @private
468
         */
469
        me.nodeContentCls = me.baseCls + '-node-content';
470

    
471
        /**
472
         * @cfg {String} downLineCls Node's down line connector class
473
         * @private
474
         */
475
        me.downLineCls = me.baseCls + '-down';
476

    
477
        /**
478
         * @cfg {String} expanderRowCls Inline expander row class
479
         * @private
480
         */
481
        me.expanderRowCls = me.baseCls + '-expander-row';
482

    
483
        /**
484
         * @cfg {String} expanderCmpCls Expander component class
485
         * @private
486
         */
487
        me.expanderCmpCls = me.baseCls + '-expander';
488

    
489
        /**
490
         * @cfg {String} addNodeCmpCls Add node component class
491
         * @private
492
         */
493
        me.addNodeCmpCls = me.baseCls + '-add';
494

    
495
        /**
496
         * @cfg {String} removeNodeCmpCls Remove node component class
497
         * @private
498
         */
499
        me.removeNodeCmpCls = me.baseCls + '-remove';
500

    
501
        /**
502
         * @cfg {String} childrenLinesCls Children connector lines row
503
         * @private
504
         */
505
        me.childrenLinesCls = me.baseCls + '-children-lines';
506

    
507
        //prepare templates
508
        me.rootTpl          = Ext.XTemplate.getTpl(this, 'rootTpl');
509
        me.nodesTpl         = Ext.XTemplate.getTpl(this, 'nodesTpl');
510
        me.nodeTpl          = Ext.XTemplate.getTpl(this, 'nodeTpl');
511
        me.innerNodeTpl     = Ext.XTemplate.getTpl(this, 'innerNodeTpl');
512
        me.downLineTpl      = Ext.XTemplate.getTpl(this, 'downLineTpl');
513
        me.childrenLinesTpl = Ext.XTemplate.getTpl(this, 'childrenLinesTpl');
514
        me.childrenLineTpl  = Ext.XTemplate.getTpl(this, 'childrenLineTpl');
515
        me.childrenTpl      = Ext.XTemplate.getTpl(this, 'childrenTpl');
516
        me.containerTpl     = Ext.XTemplate.getTpl(this, 'containerTpl');
517
        me.itemTpl          = Ext.XTemplate.getTpl(this, 'itemTpl');
518

    
519
        //adjust spacings
520
        if(me.levelSpacing < 5) me.levelSpacing = 5;
521

    
522
        //create mouse over buffer if need
523
        if (me.mouseOverOutBuffer) {
524
            me.handleMouseOverOrOut = Ext.Function.createBuffered(me.handleMouseOverOrOut, me.mouseOverOutBuffer, me);
525
            me.lastMouseOverOutEvent = new Ext.EventObjectImpl();
526
        }
527

    
528
        if (me.overItemCls) {
529
            me.trackOver = true;
530
        }
531

    
532
        this.addEvents(
533
            /**
534
             * @event beforeitemmousedown
535
             * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
536
             * @param {Ext.view.View} this
537
             * @param {Ext.data.Model} record The record that belongs to the item
538
             * @param {HTMLElement} item The item's element
539
             * @param {Number} index The item's index
540
             * @param {Ext.EventObject} e The raw event object
541
             */
542
            'beforeitemmousedown',
543
            /**
544
             * @event beforeitemmouseup
545
             * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
546
             * @param {Ext.view.View} this
547
             * @param {Ext.data.Model} record The record that belongs to the item
548
             * @param {HTMLElement} item The item's element
549
             * @param {Number} index The item's index
550
             * @param {Ext.EventObject} e The raw event object
551
             */
552
            'beforeitemmouseup',
553
            /**
554
             * @event beforeitemmouseenter
555
             * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
556
             * @param {Ext.view.View} this
557
             * @param {Ext.data.Model} record The record that belongs to the item
558
             * @param {HTMLElement} item The item's element
559
             * @param {Number} index The item's index
560
             * @param {Ext.EventObject} e The raw event object
561
             */
562
            'beforeitemmouseenter',
563
            /**
564
             * @event beforeitemmouseleave
565
             * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
566
             * @param {Ext.view.View} this
567
             * @param {Ext.data.Model} record The record that belongs to the item
568
             * @param {HTMLElement} item The item's element
569
             * @param {Number} index The item's index
570
             * @param {Ext.EventObject} e The raw event object
571
             */
572
            'beforeitemmouseleave',
573
            /**
574
             * @event beforeitemclick
575
             * Fires before the click event on an item is processed. Returns false to cancel the default action.
576
             * @param {Ext.view.View} this
577
             * @param {Ext.data.Model} record The record that belongs to the item
578
             * @param {HTMLElement} item The item's element
579
             * @param {Number} index The item's index
580
             * @param {Ext.EventObject} e The raw event object
581
             */
582
            'beforeitemclick',
583
            /**
584
             * @event beforeitemdblclick
585
             * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
586
             * @param {Ext.view.View} this
587
             * @param {Ext.data.Model} record The record that belongs to the item
588
             * @param {HTMLElement} item The item's element
589
             * @param {Number} index The item's index
590
             * @param {Ext.EventObject} e The raw event object
591
             */
592
            'beforeitemdblclick',
593
            /**
594
             * @event beforeitemcontextmenu
595
             * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
596
             * @param {Ext.view.View} this
597
             * @param {Ext.data.Model} record The record that belongs to the item
598
             * @param {HTMLElement} item The item's element
599
             * @param {Number} index The item's index
600
             * @param {Ext.EventObject} e The raw event object
601
             */
602
            'beforeitemcontextmenu',
603
            /**
604
             * @event beforeitemkeydown
605
             * Fires before the keydown event on an item is processed. Returns false to cancel the default action.
606
             * @param {Ext.view.View} this
607
             * @param {Ext.data.Model} record The record that belongs to the item
608
             * @param {HTMLElement} item The item's element
609
             * @param {Number} index The item's index
610
             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
611
             */
612
            'beforeitemkeydown',
613
            /**
614
             * @event itemmousedown
615
             * Fires when there is a mouse down on an item
616
             * @param {Ext.view.View} this
617
             * @param {Ext.data.Model} record The record that belongs to the item
618
             * @param {HTMLElement} item The item's element
619
             * @param {Number} index The item's index
620
             * @param {Ext.EventObject} e The raw event object
621
             */
622
            'itemmousedown',
623
            /**
624
             * @event itemmouseup
625
             * Fires when there is a mouse up on an item
626
             * @param {Ext.view.View} this
627
             * @param {Ext.data.Model} record The record that belongs to the item
628
             * @param {HTMLElement} item The item's element
629
             * @param {Number} index The item's index
630
             * @param {Ext.EventObject} e The raw event object
631
             */
632
            'itemmouseup',
633
            /**
634
             * @event itemmouseenter
635
             * Fires when the mouse enters an item.
636
             * @param {Ext.view.View} this
637
             * @param {Ext.data.Model} record The record that belongs to the item
638
             * @param {HTMLElement} item The item's element
639
             * @param {Number} index The item's index
640
             * @param {Ext.EventObject} e The raw event object
641
             */
642
            'itemmouseenter',
643
            /**
644
             * @event itemmouseleave
645
             * Fires when the mouse leaves an item.
646
             * @param {Ext.view.View} this
647
             * @param {Ext.data.Model} record The record that belongs to the item
648
             * @param {HTMLElement} item The item's element
649
             * @param {Number} index The item's index
650
             * @param {Ext.EventObject} e The raw event object
651
             */
652
            'itemmouseleave',
653
            /**
654
             * @event itemclick
655
             * Fires when an item is clicked.
656
             * @param {Ext.view.View} this
657
             * @param {Ext.data.Model} record The record that belongs to the item
658
             * @param {HTMLElement} item The item's element
659
             * @param {Number} index The item's index
660
             * @param {Ext.EventObject} e The raw event object
661
             */
662
            'itemclick',
663
            /**
664
             * @event itemdblclick
665
             * Fires when an item is double clicked.
666
             * @param {Ext.view.View} this
667
             * @param {Ext.data.Model} record The record that belongs to the item
668
             * @param {HTMLElement} item The item's element
669
             * @param {Number} index The item's index
670
             * @param {Ext.EventObject} e The raw event object
671
             */
672
            'itemdblclick',
673
            /**
674
             * @event itemcontextmenu
675
             * Fires when an item is right clicked.
676
             * @param {Ext.view.View} this
677
             * @param {Ext.data.Model} record The record that belongs to the item
678
             * @param {HTMLElement} item The item's element
679
             * @param {Number} index The item's index
680
             * @param {Ext.EventObject} e The raw event object
681
             */
682
            'itemcontextmenu',
683
            /**
684
             * @event itemkeydown
685
             * Fires when a key is pressed while an item is currently selected.
686
             * @param {Ext.view.View} this
687
             * @param {Ext.data.Model} record The record that belongs to the item
688
             * @param {HTMLElement} item The item's element
689
             * @param {Number} index The item's index
690
             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
691
             */
692
            'itemkeydown',
693
            /**
694
             * @event beforecontainermousedown
695
             * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
696
             * @param {Ext.view.View} this
697
             * @param {Ext.EventObject} e The raw event object
698
             */
699
            'beforecontainermousedown',
700
            /**
701
             * @event beforecontainermouseup
702
             * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
703
             * @param {Ext.view.View} this
704
             * @param {Ext.EventObject} e The raw event object
705
             */
706
            'beforecontainermouseup',
707
            /**
708
             * @event beforecontainermouseover
709
             * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
710
             * @param {Ext.view.View} this
711
             * @param {Ext.EventObject} e The raw event object
712
             */
713
            'beforecontainermouseover',
714
            /**
715
             * @event beforecontainermouseout
716
             * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
717
             * @param {Ext.view.View} this
718
             * @param {Ext.EventObject} e The raw event object
719
             */
720
            'beforecontainermouseout',
721
            /**
722
             * @event beforecontainerclick
723
             * Fires before the click event on the container is processed. Returns false to cancel the default action.
724
             * @param {Ext.view.View} this
725
             * @param {Ext.EventObject} e The raw event object
726
             */
727
            'beforecontainerclick',
728
            /**
729
             * @event beforecontainerdblclick
730
             * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
731
             * @param {Ext.view.View} this
732
             * @param {Ext.EventObject} e The raw event object
733
             */
734
            'beforecontainerdblclick',
735
            /**
736
             * @event beforecontainercontextmenu
737
             * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
738
             * @param {Ext.view.View} this
739
             * @param {Ext.EventObject} e The raw event object
740
             */
741
            'beforecontainercontextmenu',
742
            /**
743
             * @event beforecontainerkeydown
744
             * Fires before the keydown event on the container is processed. Returns false to cancel the default action.
745
             * @param {Ext.view.View} this
746
             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
747
             */
748
            'beforecontainerkeydown',
749
            /**
750
             * @event containermouseup
751
             * Fires when there is a mouse up on the container
752
             * @param {Ext.view.View} this
753
             * @param {Ext.EventObject} e The raw event object
754
             */
755
            'containermouseup',
756
            /**
757
             * @event containermouseover
758
             * Fires when you move the mouse over the container.
759
             * @param {Ext.view.View} this
760
             * @param {Ext.EventObject} e The raw event object
761
             */
762
            'containermouseover',
763
            /**
764
             * @event containermouseout
765
             * Fires when you move the mouse out of the container.
766
             * @param {Ext.view.View} this
767
             * @param {Ext.EventObject} e The raw event object
768
             */
769
            'containermouseout',
770
            /**
771
             * @event containerclick
772
             * Fires when the container is clicked.
773
             * @param {Ext.view.View} this
774
             * @param {Ext.EventObject} e The raw event object
775
             */
776
            'containerclick',
777
            /**
778
             * @event containerdblclick
779
             * Fires when the container is double clicked.
780
             * @param {Ext.view.View} this
781
             * @param {Ext.EventObject} e The raw event object
782
             */
783
            'containerdblclick',
784
            /**
785
             * @event containercontextmenu
786
             * Fires when the container is right clicked.
787
             * @param {Ext.view.View} this
788
             * @param {Ext.EventObject} e The raw event object
789
             */
790
            'containercontextmenu',
791
            /**
792
             * @event containerkeydown
793
             * Fires when a key is pressed while the container is focused, and no item is currently selected.
794
             * @param {Ext.view.View} this
795
             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
796
             */
797
            'containerkeydown',
798

    
799
            /**
800
             * @event
801
             * @inheritdoc Ext.selection.DataViewModel#selectionchange
802
             */
803
            'selectionchange',
804
            /**
805
             * @event
806
             * @inheritdoc Ext.selection.DataViewModel#beforeselect
807
             */
808
            'beforeselect',
809
            /**
810
             * @event
811
             * @inheritdoc Ext.selection.DataViewModel#beforedeselect
812
             */
813
            'beforedeselect',
814
            /**
815
             * @event
816
             * @inheritdoc Ext.selection.DataViewModel#select
817
             */
818
            'select',
819
            /**
820
             * @event
821
             * @inheritdoc Ext.selection.DataViewModel#deselect
822
             */
823
            'deselect',
824
            /**
825
             * @event
826
             * @inheritdoc Ext.selection.DataViewModel#focuschange
827
             */
828
            'focuschange',
829

    
830
            /**
831
             * @event highlightitem
832
             * Fires when a node is highlighted using keyboard navigation, or mouseover.
833
             * @param {Ext.view.View} view This View Component.
834
             * @param {Ext.Element} node The highlighted node.
835
             */
836
            'highlightitem',
837

    
838
            /**
839
             * @event unhighlightitem
840
             * Fires when a node is unhighlighted using keyboard navigation, or mouseout.
841
             * @param {Ext.view.View} view This View Component.
842
             * @param {Ext.Element} node The previously highlighted node.
843
             */
844
            'unhighlightitem',
845

    
846
            /**
847
             * @event additem
848
             * Fires when one of add tools is clicked.
849
             * @param {Ext.view.View} view This View Component.
850
             * @param {Ext.data.NodeInterface} record The reference record for add action.
851
             * @param {"before/after/child"} where Where to add the new record in relation to the reference record.
852
             * @param {Ext.dom.Element} node The node element.
853
             */
854
            'additem',
855

    
856
            /**
857
             * @event removeitem
858
             * Fires when remove item tool is clicked.
859
             * @param {Ext.view.View} view This View Component.
860
             * @param {Ext.data.NodeInterface} record The reference record for remove action.
861
             * @param {Ext.dom.Element/null} node The node element.
862
             */
863
            'removeitem',
864

    
865
            /**
866
             * @event itemupdate
867
             * Fires when one of nodes has been changed.
868
             * @param {Ext.view.View} view This View Component.
869
             * @param {Ext.data.NodeInterface} record The reference record for add action.
870
             * @param {Ext.dom.Element} node The node element.
871
             */
872
            'itemupdate'
873
        );
874

    
875
        //create store if needed
876
        if(Ext.isString(store)){
877
            //it's an store id
878
            store = Ext.StoreManager.lookup(store);
879
        }
880
        else if( !store || Ext.isObject(store) && !store.isStore){
881
            //it's an store object declaration
882
            store = me.store = new Ext.data.TreeStore(Ext.apply({
883
                root: root,
884
                fields: me.fields,
885
                model: me.model
886
            }), store);
887

    
888

    
889
        }
890
        else if (root) {
891
            store = me.store = Ext.data.StoreManager.lookup(store);
892
        }
893

    
894
        //sets the root node
895
        me.root = root || store.getRootNode();
896

    
897
        //binds the store
898
        me.bindStore(store, true, 'dataStore');
899
        me.callParent(arguments);
900
    },
901

    
902
    /** @inheritdoc */
903
    onRender: function(){
904
        var me = this;
905
        me.callParent(arguments);
906
        me.el.ddScrollConfig= {
907
            vthresh: 25,
908
            hthresh: 25,
909
            frequency: 300,
910
            increment: 100
911
        };
912
    },
913

    
914
    beforeRender: function() {
915
        var me = this;
916
        me.callParent(arguments);
917
        //me.protoEl.set('unselectable', true);
918
        me.getSelectionModel().beforeViewRender(me);
919
    },
920

    
921
    afterRender: function(){
922
        var me = this,
923
            store = this.store,
924
            onMouseOverOut = me.mouseOverOutBuffer ? me.onMouseOverOut : me.handleMouseOverOrOut;
925
        me.callParent();
926

    
927
        //todo handle keyboard an context menu
928
        //init component input event handler
929
        me.mon( me.el, {
930
            scope      : me,
931
            /*
932
             * We need to make copies of this since some of the events fired here will end up triggering
933
             * a new event to be called and the shared event object will be mutated. In future we should
934
             * investigate if there are any issues with creating a new event object for each event that
935
             * is fired.
936
             */
937
            freezeEvent: true,
938
            click      : me.handleEvent,
939
            mousedown  : me.handleEvent,
940
            mouseup    : me.handleEvent,
941
            dblclick   : me.handleEvent,
942
            contextmenu: me.handleEvent,
943
            keydown    : me.handleEvent,
944
            mouseover  : me.onMouseOverOut,
945
            mouseout   : me.onMouseOverOut
946
        } );
947

    
948
        //there's not a root node?
949
        if( !me.store.getCount() ){
950
            //load the store if need and postpone rendering
951
            if( !store.isLoading() && me.autoLoadStore){
952
                store.load();
953
            }
954
        }
955
        else{
956
            this.refresh();
957
        }
958

    
959
        this.getSelectionModel().bindComponent(this);
960

    
961
        //inline expander tool config
962
        me.el.on({
963
            scope: me,
964
            delegate: me.expanderSelector,
965
            mouseover: me.onExpanderMouseOver,
966
            mouseout: me.onExpanderMouseOut,
967
            click: me.onInlineExpanderClick
968
        });
969
        //init floating tools
970
        me.expandTool = Ext.create('Ext.Component',{
971
            floating: true,
972
            border: 0,
973
            autoEl: {
974
               tag: 'div',
975
               'data-qtip': Ext.util.Format.htmlEncode(me.collapseTip)
976
            },
977
            shadow: false,
978
            hidden: true,
979
            width: 16,
980
            height: 16,
981
            cls: me.expanderCmpCls + (me.expanderCls ? ' '+ me.expanderCls : ''),
982
            listeners:{
983
                scope: me,
984
                render: function(){
985
                    var el = this.expandTool.getEl();
986
                    el.on('click', this.onItemExpandClick, this);
987
                }
988
            }
989
        });
990
        me.addBeforeTool = Ext.create('Ext.Component',{
991
            floating: true,
992
            border: 0,
993
            autoEl: {
994
                tag: 'div',
995
                'data-qtip': Ext.util.Format.htmlEncode(me.addBeforeTip)
996
            },
997
            shadow: false,
998
            hidden: true,
999
            width: 16,
1000
            height: 16,
1001
            cls: me.addNodeCmpCls + (me.addBeforeCls ? ' '+ me.addBeforeCls : ''),
1002
            listeners:{
1003
                scope: me,
1004
                render: function(){
1005
                    var el = this.addBeforeTool.getEl();
1006
                    this.mon(el, 'click', this.onItemAddBeforeClick, this);
1007
                }
1008
            }
1009
        });
1010

    
1011
        me.addAfterTool = Ext.create('Ext.Component',{
1012
            floating: true,
1013
            border: 0,
1014
            autoEl: {
1015
                tag: 'div',
1016
                'data-qtip': Ext.util.Format.htmlEncode(me.addAfterTip)
1017
            },
1018
            shadow: false,
1019
            hidden: true,
1020
            width: 16,
1021
            height: 16,
1022
            cls: me.addNodeCmpCls + (me.addAfterCls ? ' '+ me.addAfterCls : ''),
1023
            listeners:{
1024
                scope: me,
1025
                render: function(){
1026
                    var el = this.addAfterTool.getEl();
1027
                    this.mon(el, 'click', this.onItemAddAfterClick, this);
1028
                }
1029
            }
1030
        });
1031

    
1032
        me.addChildTool = Ext.create('Ext.Component',{
1033
            floating: true,
1034
            border: 0,
1035
            autoEl: {
1036
                tag: 'div',
1037
                'data-qtip': Ext.util.Format.htmlEncode(me.addChildTip)
1038
            },
1039
            shadow: false,
1040
            hidden: true,
1041
            width: 16,
1042
            height: 16,
1043
            cls: me.addNodeCmpCls + (me.addChildCls ? ' '+ me.addChildCls : ''),
1044
            listeners:{
1045
                scope: me,
1046
                render: function(){
1047
                    var el = this.addChildTool.getEl();
1048
                    this.mon(el, 'click', this.onItemAddChildClick, this);
1049
                }
1050
            }
1051
        });
1052

    
1053
        me.removeItemTool = Ext.create('Ext.Component',{
1054
            floating: true,
1055
            border: 0,
1056
            autoEl: {
1057
                tag: 'div',
1058
                'data-qtip': Ext.util.Format.htmlEncode(me.removeItemTip)
1059
            },
1060
            shadow: false,
1061
            hidden: true,
1062
            width: 16,
1063
            height: 16,
1064
            cls: me.removeNodeCmpCls + (me.removeItemCls ? ' '+ me.removeItemCls : ''),
1065
            listeners:{
1066
                scope: me,
1067
                render: function(){
1068
                    var el = this.removeItemTool.getEl();
1069
                    this.mon(el, 'click', this.onItemRemoveClick, this);
1070
                }
1071
            }
1072
        });
1073
    },
1074

    
1075
    /** @inheritdoc */
1076
    onDestroy: function(){
1077
        var me = this;
1078

    
1079
        //unbind store
1080
        me.bindStore( null );
1081

    
1082
        //fre component input handler
1083
        me.mun( me.el, {
1084
            scope      : me,
1085
            /*
1086
             * We need to make copies of this since some of the events fired here will end up triggering
1087
             * a new event to be called and the shared event object will be mutated. In future we should
1088
             * investigate if there are any issues with creating a new event object for each event that
1089
             * is fired.
1090
             */
1091
            freezeEvent: true,
1092
            click      : me.handleEvent,
1093
            mousedown  : me.handleEvent,
1094
            mouseup    : me.handleEvent,
1095
            dblclick   : me.handleEvent,
1096
            //contextmenu: me.handleEvent,
1097
            //keydown    : me.handleEvent,
1098
            mouseover  : me.onMouseOverOut,
1099
            mouseout   : me.onMouseOverOut
1100
        } );
1101

    
1102
        //free inline expand tool handler
1103
        me.el.un( {
1104
            scope    : me,
1105
            delegate : me.expanderSelector,
1106
            mouseover: me.onExpanderMouseOver,
1107
            mouseout : me.onExpanderMouseOut,
1108
            click    : me.onInlineExpanderClick
1109
        } );
1110

    
1111
        //fre the floating tools handlers
1112
        if(me.expandTool){
1113
            me.mun(me.expandTool.getEl(), 'click', me.onItemExpandClick, me);
1114
            me.expandTool.destroy();
1115
        }
1116
        if(me.addBeforeTool){
1117
            me.mun(me.addBeforeTool.getEl(), 'click', me.onItemAddBeforeClick, me);
1118
            me.addBeforeTool.destroy();
1119
        }
1120
        if(me.addAfterTool){
1121
            me.mun(me.addAfterTool.getEl(), 'click', me.onItemAddAfterClick, me);
1122
            me.addAfterTool.destroy();
1123
        }
1124
        if(me.addChildTool){
1125
            me.mun(me.addChildTool.getEl(), 'click', me.onItemAddChildClick, me);
1126
            me.addChildTool.destroy();
1127
        }
1128
        if(me.removeItemTool){
1129
            me.mun(me.removeItemTool.getEl(), 'click', me.onItemRemoveClick, me);
1130
            me.removeItemTool.destroy();
1131
        }
1132

    
1133
        //free the selection model
1134
        if( me.selModel ){
1135
            me.selModel.destroy();
1136
        }
1137

    
1138

    
1139
        me.callParent( arguments );
1140
    },
1141

    
1142
    /**
1143
     * Sets the root node for the component
1144
     * @param node
1145
     */
1146
    setRootNode: function(node) {
1147
        var me = this
1148
            store = this.store;
1149
        this.root = node;
1150

    
1151
        //if root is set by loading store, postpone for onload event
1152
        if(!store.isLoading() && store.getCount() && me.initialLoad) this.refresh();
1153
    },
1154

    
1155
    /**
1156
     * Returns the root node for this instance
1157
     *
1158
     * @returns {Ext.data.NodeInterface/null} The root node
1159
     */
1160
    getRootNode: function(){
1161
        return this.root;
1162
    },
1163

    
1164
    /** @inheritdoc */
1165
    onBindStore: function(store, initial){
1166
        var me = this;
1167
        me.store = store;
1168
        me.initialLoad = false;
1169

    
1170
        // Bind the store to our selection model unless it's the initial bind.
1171
        // Initial bind takes place in afterRender
1172
        // Same for first rendering
1173
        if (!initial) {
1174
            me.getSelectionModel().bindStore(store);
1175
            me.refresh();
1176
        }
1177
    },
1178

    
1179
    /** @inheritdoc */
1180
    onUnbindStore: function(store, initial){
1181
        var me = this;
1182
        me.store = null;
1183
    },
1184

    
1185
    /** @inheritdoc */
1186
    getStoreListeners: function(){
1187
        var me = this,
1188
            listeners = {
1189
                idchanged     : me.onStoreIdChanged,
1190
                load          : me.onStoreLoad,
1191
                beforeload    : me.onStoreBeforeLoad,
1192
                rootchange    : me.onStoreRootChange,
1193
                beforeexpand  : me.onStoreBeforeExpand,
1194
                expand        : me.onStoreExpand,
1195
                beforecollapse: me.onStoreBeforeCollapse,
1196
                collapse      : me.onStoreCollapse,
1197
                //datachanged   : me.onStoreDataChanged,
1198
                append        : me.onStoreAppend,
1199
                insert        : me.onStoreInsert,
1200
                clear         : me.onStoreClear,
1201
                refresh       : me.onDataRefresh,
1202
                update        : me.onStoreUpdate
1203
            };
1204

    
1205
        listeners['remove'] = me.onStoreRemove;
1206
        if( Ext.versions.extjs.isLessThan( '4.2.0' ) ){
1207
            //there´s a bug in code below 4.2.0, update event is not fired for treestore
1208
            //so use a flag to solve it in the component code
1209
            me.needNodeJoin = true;
1210
        }
1211
        else{
1212
            listeners['bulkremove'] = me.onStoreBulkRemove;
1213
        }
1214

    
1215
        return listeners;
1216
    },
1217

    
1218
    /**
1219
     * Draws/Redraws the full component's content
1220
     */
1221
    refresh: function(){
1222
        var me = this,
1223
            el = me.el,
1224
            store = me.store,
1225
            root = me.root,
1226
            body, out;
1227

    
1228
        //not rendered yet or already refreshing, so postpone
1229
        if(!me.rendered || me.refreshingView) return;
1230

    
1231
        body = me.getChildrenContainer(me.el);
1232

    
1233
        me.hideTools();
1234

    
1235
        //its rendered but doesnt have associated data??
1236
        //so clear the component
1237
        if(!store || !root){
1238
            me.renderTpl.overwrite(me.el,{});
1239
            return;
1240
        }
1241

    
1242
        me.suspendEvents();
1243

    
1244
        me.refreshingView = true;
1245

    
1246
        me.initialLoad = true;
1247

    
1248
        out = ['<table class="'+me.wrapperCls+'"><tr class="'+me.containerCls+'">'];
1249
        me.renderNodes(me.rootVisible ? [root] : root.childNodes, out);
1250
        out.push('</tr></table>');
1251

    
1252
        Ext.fly(me.el).setHTML(out.join(''));
1253

    
1254
        me.refreshingView = false;
1255

    
1256
        me.resumeEvents();
1257
    },
1258

    
1259
    /**
1260
     *
1261
     * @param root
1262
     *
1263
     * @todo Precisa acertar, pois o nó pode não ser a raiz da arvore e pode ser necessário ler até encontrar
1264
     */
1265
    onStoreRootChange: function(root) {
1266
        var me = this;
1267
        //there is a bug with update event for ext below 4.2.0
1268
        //so we need fire this events any way
1269
        if(!me.needNodeJoin){
1270
            me.store.un( 'append', me.onStoreAppend, me );
1271
            me.store.un( 'update', me.onStoreUpdate, me );
1272
        }
1273
        me.storeRootLoad = true;
1274
        me.setRootNode(root);
1275
    },
1276

    
1277
    onStoreBeforeExpand: function(record){
1278
        var me = this,
1279
            item = me.getItemByRecord(record);
1280

    
1281
        if(!item) return;
1282
        me.nodeRegionOnExpand = Ext.fly(item).getRegion();
1283
    },
1284

    
1285
    /**
1286
     * Expands a record that is loaded in the view.
1287
     *
1288
     * If an animated collapse or expand of the record is in progress, this call will be ignored.
1289
     * @param {Ext.data.Model} record The record to expand
1290
     * @param {Boolean} [deep] True to expand nodes all the way down the tree hierarchy.
1291
     * @param {Function} [callback] The function to run after the expand is completed
1292
     * @param {Object} [scope] The scope of the callback function.
1293
     */
1294
    expand: function(record, deep, callback, scope) {
1295
        var me = this,
1296
            result;
1297

    
1298
        // Need to suspend layouts because the expand process makes multiple changes to the UI
1299
        // in addition to inserting new nodes. Folder and elbow images have to change, so we
1300
        // need to coalesce all resulting layouts.
1301
        Ext.suspendLayouts();
1302
        result = record.expand(deep, callback, scope);
1303
        Ext.resumeLayouts(true);
1304
        return result;
1305
    },
1306

    
1307
    collapse: function(record, deep, callback, scope) {
1308
        var me = this,
1309
            result;
1310

    
1311
        // Need to suspend layouts because the expand process makes multiple changes to the UI
1312
        // in addition to inserting new nodes. Folder and elbow images have to change, so we
1313
        // need to coalesce all resulting layouts.
1314
        Ext.suspendLayouts();
1315
        result = record.collapse(deep, callback, scope);
1316
        Ext.resumeLayouts(true);
1317
        return result;
1318
    },
1319

    
1320
    onStoreExpand: function(record){
1321
        var me = this,
1322
            item = me.getItemByRecord(record),
1323
            node,
1324
            cls = me.collapseCls,
1325
            ct;
1326

    
1327
        //verify if record is in the component
1328
        if(!item) return;
1329

    
1330
        node = me.getNodeFromChildEl(item);
1331

    
1332
        if(!node) return;
1333

    
1334
        node.removeCls(cls);
1335

    
1336
        if(me.nodeRegionOnExpand){
1337
            me.preserveScroll(item);
1338
        }
1339
        else me.focusNode(record);
1340
        me.nodeRegionOnExpand = null;
1341
    },
1342

    
1343
    onStoreBeforeCollapse: function(record){
1344
        var me = this,
1345
            item = me.getItemByRecord(record);
1346

    
1347
        if(!item) return;
1348
        me.nodeRegionOnExpand = Ext.fly(item).getRegion();
1349
    },
1350

    
1351
    preserveScroll: function(node){
1352
        var me = this,
1353
            region = Ext.fly(node).getRegion(),
1354
            oldRegion = me.nodeRegionOnExpand,
1355
            diffX, diffY;
1356

    
1357
        diffY = region.top - oldRegion.top;
1358
        diffX = region.left - oldRegion.left;
1359

    
1360
        me.scrollBy(diffX, diffY);
1361

    
1362
    },
1363

    
1364
    onStoreCollapse: function(record){
1365
        var me = this,
1366
            item = me.getItemByRecord(record),
1367
            node,
1368
            cls = me.collapseCls,
1369
            ct;
1370

    
1371
        //verify if record is in the component
1372
        if(!item) return;
1373

    
1374
        node = me.getNodeFromChildEl(item);
1375

    
1376
        if(!node) return;
1377

    
1378
        node.addCls(cls);
1379
        if(me.nodeRegionOnExpand){
1380
            me.preserveScroll(item);
1381
        }
1382
        else me.focusNode(record);
1383
        me.nodeRegionOnExpand = null;
1384
    },
1385

    
1386
    onStoreIdChanged: function(store, rec, oldId, newId, oldInternalId){
1387
        var me = this,
1388
            nodeDom;
1389

    
1390
        if (me.rendered) {
1391
            nodeDom = me.getNode(oldId || oldInternalId);
1392
            if (nodeDom) {
1393
                nodeDom.setAttribute('data-recordId', me.getRecordId(rec));
1394
                nodeDom.id = me.getNodeId(rec);
1395
            }
1396
        }
1397
    },
1398

    
1399
    /*onStoreDataChanged: function(){
1400
        console.log('changed');
1401
    },*/
1402

    
1403
    onStoreBeforeLoad: function(store, operation, options){
1404
        var me = this,
1405
            loadMask = me.loadMask,
1406
            nodeItem, node;
1407

    
1408
        if(me.rendered && loadMask){
1409
            if(Ext.isObject(loadMask)){
1410
                loadMask.show();
1411
            }
1412
            else{
1413
                nodeItem = me.getItemByRecord(operation.node);
1414
                if(!nodeItem) return;
1415
                node = me.getNodeFromChildEl(nodeItem);
1416
                node = me.getNodeInlineExpanderContainer(node);
1417
                Ext.fly(node).mask(me.loadingText);
1418
            }
1419
        }
1420
    },
1421

    
1422
    onStoreLoad: function(store, fillRoot, newNodes) {
1423
        var me = this,
1424
            root = me.root,
1425
            loadMask = me.loadMask,
1426
            parent, node, nodeItem;
1427

    
1428
        // Always update the current node, since the load may be triggered
1429
        // by .load() directly instead of .expand() on the node
1430
        fillRoot.triggerUIUpdate();
1431

    
1432
        me.storeRootLoad = false;
1433
        if(!me.needNodeJoin){
1434
            me.store.on('append',me.onStoreAppend, me);
1435
            me.store.on('update',me.onStoreUpdate, me);
1436
        }
1437

    
1438
        if(!me.rendered) return;
1439

    
1440
        if(loadMask){
1441
            if(Ext.isObject(loadMask)){
1442
                loadMask.hide();
1443
            }
1444
            else{
1445
                nodeItem = me.getItemByRecord(fillRoot);
1446
                if(nodeItem){
1447
                    node = me.getNodeFromChildEl(nodeItem);
1448
                    node = me.getNodeInlineExpanderContainer(node);
1449
                    Ext.fly(node).unmask();
1450
                }
1451
            }
1452
        }
1453

    
1454
        if(fillRoot == root){
1455
            me.refresh();
1456
            return;
1457
        }
1458

    
1459
        if(root != store.getRootNode()){
1460
            //not all nodes of store belongs to the component
1461
            //so we need figure out if the loaded nodes belongs to component's root
1462
            parent = fillRoot.parentNode;
1463
            while(parent){
1464
                if(parent == root) break;
1465
                parent = parent.parentNode;
1466
            }
1467

    
1468
            if(parent != root) return;
1469
        }
1470
        me.refreshNode(fillRoot);
1471
    },
1472

    
1473

    
1474
    getRecordId: function(record){
1475
        return (record.get('id') || record.internalId);
1476
    },
1477

    
1478
    /**
1479
     * Get node's id to use for node item HTML element
1480
     * @param {Ext.data.NodeInterface} record The record that the element represents
1481
     * @returns {String} The element id
1482
     */
1483
    getNodeId: function(record){
1484
        return this.id + '-record-' + this.getRecordId(record);
1485
    },
1486

    
1487
    /**
1488
     * Gets the record for the node item HTML element
1489
     *
1490
     * @param {HtmlElement} node The html element that represents the record
1491
     * @returns {Ext.data.NodeInterface} The record if found
1492
     */
1493
    getRecord: function(node) {
1494
        node = this.getNode(node);
1495
        if(node) {
1496
            var id = node.getAttribute('data-recordId');
1497
            if(!id && Ext.isIE){
1498
                id = node.id.substr(this.id.length+8);
1499
            }
1500
            return this.store.getNodeById(id);
1501
        }
1502
    },
1503

    
1504
    /**
1505
     * Gets a template node item.
1506
     *
1507
     * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo An HTMLElement template node,
1508
     * the id of a template node or the record associated with the node.
1509
     *
1510
     * @return {HTMLElement} The node or null if it wasn't found
1511
     */
1512
    getNode : function(nodeInfo) {
1513
        if ((!nodeInfo && nodeInfo !== 0) || !this.rendered) {
1514
            return null;
1515
        }
1516

    
1517
        if (Ext.isString(nodeInfo)) {
1518
            return document.getElementById(nodeInfo);
1519
        }
1520

    
1521
        if (nodeInfo.isModel) {
1522
            return this.getItemByRecord(nodeInfo);
1523
        }
1524

    
1525
        return nodeInfo; // already an HTMLElement
1526
    },
1527

    
1528
    /**
1529
     * Get the nodes on this component
1530
     *
1531
     * @returns {Array} The nodes list
1532
     *
1533
     * @todo implement this method
1534
     */
1535
    getNodes: function(){
1536
        return [];
1537
    },
1538

    
1539
    /**
1540
     * Gets a template node content or node container template from a record
1541
     *
1542
     * @param {Ext.data.NodeInterface} record The record to find the node template
1543
     * @param {Boolean} container When true the node's container element is returned if found, if false the node's content element
1544
     *
1545
     * @returns {HtmlElement}
1546
     */
1547
    getItemByRecord: function(record, container) {
1548
        var id = this.getNodeId(record);
1549
        return this.retrieveNode(id, container);
1550
    },
1551

    
1552
    /**
1553
     * Gets a template node content or node container template from it's id
1554
     *
1555
     * @param {Ext.data.NodeInterface} record The record to find the node template
1556
     * @param {Boolean} container When true the node's container element is returned if found, if false the node's content element
1557
     *
1558
     * @returns {HtmlElement}
1559
     *
1560
     * @protected
1561
     */
1562
    retrieveNode: function(id, container){
1563
        var me = this,
1564
            result = me.el.getById(id, true),
1565
            fly;
1566

    
1567
        if(container && result) {
1568
            if (!(fly = Ext.fly(result)).is(me.itemSelector)) {
1569
                return me.getNodeFromChildEl(fly);
1570
            }
1571
        }
1572

    
1573
        return result;
1574
    },
1575

    
1576
    /**
1577
     * Gets the node's container for the node content
1578
     *
1579
     * @param {HTMLElement|Ext.dom.Element} node The node element
1580
     *
1581
     * @returns {Ext.dom.Element/null} The content container if the node contains it
1582
     */
1583
    getItemContainer: function(node){
1584
        return Ext.fly(node).child(this.nodeItemContainerSelector);
1585
    },
1586

    
1587
    /**
1588
     * Gets the node content container that contains the item
1589
     *
1590
     * @param {HTMLElement|Ext.dom.Element} item The item contents element
1591
     *
1592
     * @returns {Ext.dom.Element/null} The content row if found
1593
     */
1594
    getItemRowFromItem: function(item){
1595
        return Ext.fly(item).parent(this.itemRowSelector);
1596
    },
1597

    
1598
    /**
1599
     * Get the node element from one of its children elements
1600
     *
1601
     * @param {HTMLElement|Ext.dom.Element} item The node's child element
1602
     *
1603
     * @returns {Ext.dom.Element/null} The node element if found
1604
     */
1605
    getNodeFromChildEl: function(item){
1606
        return Ext.fly(item).up(this.nodeSelector);
1607
    },
1608

    
1609
    /**
1610
     * Get the node's body element
1611
     *
1612
     * @param {HTMLElement|Ext.dom.Element} node The node element
1613
     *
1614
     * @returns {Ext.dom.Element|null} The body of the node element if found
1615
     */
1616
    getNodeBody: function(node){
1617
        return Ext.fly(node).child(this.nodeBodySelector);
1618
    },
1619

    
1620
    /**
1621
     * Get the node's children container
1622
     *
1623
     * @param {HTMLElement|Ext.dom.Element} node The node element
1624
     *
1625
     * @returns {Ext.dom.Element|null} The children container element
1626
     */
1627
    getChildrenContainer: function(node){
1628
        return Ext.fly(node).child(this.nodeContainerSelector);
1629
    },
1630

    
1631
    /**
1632
     * Get the container of the children connector lines
1633
     *
1634
     * @param {HTMLElement|Ext.dom.Element} node The node element
1635
     *
1636
     * @returns {Ext.dom.Element|null} The container for the children lines of the node
1637
     */
1638
    getChildrenLinesContainer: function(node){
1639
        return Ext.fly(node).child(this.childrenLinesSelector);
1640
    },
1641

    
1642
    /**
1643
     * Get the node's wrapper for the down line connector
1644
     *
1645
     * @param {HTMLElement|Ext.dom.Element} node The node element
1646
     *
1647
     * @returns {Ext.dom.Element|null} The node's down line connector wrapper
1648
     */
1649
    getNodeDownLine: function(node){
1650
        return Ext.fly(node).down(this.downLineSelector);
1651
    },
1652

    
1653
    /**
1654
     * Get the node container for the down line connector
1655
     *
1656
     * @param {HTMLElement|Ext.dom.Element} node The node element
1657
     *
1658
     * @returns {Ext.dom.Element|null} The node's down line connector container
1659
     */
1660
    getNodeDownLineContainer: function(node){
1661
        return Ext.fly(node).down(this.downLineContainerSelector);
1662
    },
1663

    
1664
    /**
1665
     * Get the node container for the inline expander
1666
     *
1667
     * @param {HTMLElement|Ext.dom.Element} node The node element
1668
     *
1669
     * @returns {Ext.dom.Element|null} The node's down line connector wrapper
1670
     */
1671
    getNodeInlineExpanderContainer: function(node){
1672
        return Ext.fly(node).child(this.inlineExpanderContainerSelector);
1673
    },
1674

    
1675
    /**
1676
     * Get the node wrapper for the inline expander
1677
     *
1678
     * @param {HTMLElement|Ext.dom.Element} node The node element
1679
     *
1680
     * @returns {Ext.dom.Element|null} The node's down line connector wrapper
1681
     */
1682
    getNodeInlineExpander: function(node){
1683
        return Ext.fly(node).child(this.inlineExpanderContentSelector);
1684
    },
1685

    
1686

    
1687
    /**
1688
     * Removes a node from the component using the record data
1689
     *
1690
     * @param {Ext.data.NodeInterface} record The node record
1691
     *
1692
     * @return {HtmlElement} The node element removed
1693
     */
1694
    removeNodeFromRecord: function(record){
1695
        var me = this,
1696
            item = me.getItemByRecord(record),
1697
            node, parent, count, tmp;
1698

    
1699
        if(!item) return null;
1700

    
1701
        node = me.getNodeFromChildEl(item);
1702
        parent = me.getNodeFromChildEl(node);
1703
        me.getSelectionModel().deselect(record);
1704
        //Ext.fly(item).removeCls('x-item-selected');
1705
        Ext.fly(node).remove();
1706

    
1707
        //could be a root node child and root isn't shown in the component
1708
        if(parent){
1709
            //adjust the parent node to reflect the removal
1710
            tmp = me.getNodeDownLine(parent);
1711
            count = tmp.getAttribute('colSpan') - 1;
1712
            if(!count){
1713
                //become a leaf, so remove unnecessary elements (down line, inline expander, child nodes container, etc..)
1714
                tmp.parent().remove();
1715
                me.getNodeInlineExpanderContainer(parent).remove();
1716
                me.getChildrenContainer(parent).remove();
1717
            }
1718
            else{
1719
                //adjust the parent node
1720
                tmp.set({colSpan: count});
1721
                me.getNodeInlineExpander(parent).set({colSpan : count});
1722
                tmp = me.getChildrenLinesContainer(parent).dom;
1723
                //removes the child connector line
1724
                if(count < 2){
1725
                    //remove the full container of children connector lines
1726
                    Ext.fly(tmp).remove();
1727
                }
1728
                else{
1729
                    //removes just the connector line of one child
1730
                    Ext.fly(tmp.childNodes[1]).remove();
1731
                }
1732
            }
1733
        }
1734

    
1735
        return node;
1736
    },
1737

    
1738
    /**
1739
     * Removes a node from the component
1740
     *
1741
     * @param {Ext.data.NodeInterface} record The node element
1742
     *
1743
     * @return {HtmlElement} The node element removed
1744
     */
1745
    removeNode: function(node){
1746
        var me = this,
1747
            parent, count, tmp;
1748

    
1749
        if(!node) return null;
1750

    
1751
        parent = me.getNodeFromChildEl(node);
1752
        Ext.fly(node).remove();
1753

    
1754
        //could be a root node child and root isn't shown in the component
1755
        if(parent){
1756
            //adjust the parent node to reflect the removal
1757
            tmp = me.getNodeDownLine(parent);
1758
            count = tmp.getAttribute('colSpan') - 1;
1759
            if(!count){
1760
                //become a leaf, so remove unnecessary elements (down line, inline expander, child nodes container, etc..)
1761
                tmp.parent().remove();
1762
                me.getNodeInlineExpanderContainer(parent).remove();
1763
                me.getChildrenContainer(parent).remove();
1764
            }
1765
            else{
1766
                //adjust the parent node
1767
                tmp.set({colSpan: count});
1768
                me.getNodeInlineExpander(parent).set({colSpan : count});
1769
                tmp = me.getChildrenLinesContainer(parent).dom;
1770
                //removes the child connector line
1771
                if(count < 2){
1772
                    //remove the full container of children connector lines
1773
                    Ext.fly(tmp).remove();
1774
                }
1775
                else{
1776
                    //removes just the connector line of one child
1777
                    Ext.fly(tmp.childNodes[1]).remove();
1778
                }
1779
            }
1780
        }
1781

    
1782
        return node;
1783
    },
1784

    
1785
    /**
1786
     * Rebuild the full node structure
1787
     *
1788
     * @param record The record to have it's structure rebuilded
1789
     */
1790
    refreshNode: function(record){
1791
        var me = this,
1792
            nodeItem = me.getItemByRecord(record),
1793
            node;
1794

    
1795
        //the node was not found?
1796
        if(!nodeItem  ){
1797
            return;
1798
        }
1799

    
1800
        node = me.getNodeFromChildEl(nodeItem);
1801
        if(!node){
1802
            return;
1803
        }
1804

    
1805
        me.innerNodeTpl.overwrite(node, {view: me, node: record, nodeId: me.getNodeId(record)});
1806
    },
1807

    
1808
    insertNodeFromRecord: function(record, refRecord){
1809
        var me = this,
1810
            parentRecord = record.parentNode,
1811
            node,nodeItem,
1812
            inWrapper = false,
1813
            childCount = parentRecord.childNodes.length,
1814
            renderBuffer = me.renderBuffer,
1815
            out, container, body, len, tmp, parentItem,
1816
            parentNode, values, downLine, reference;
1817

    
1818
        if(!me.rendered){
1819
            return;
1820
        }
1821

    
1822
        nodeItem = me.getItemByRecord(record);
1823
        if(nodeItem){
1824
            node = me.getNodeFromChildEl(nodeItem);
1825
        }
1826

    
1827
        parentItem = me.getItemByRecord(parentRecord);
1828

    
1829
        //insert at root but root could not be visible, so add to component wrapper
1830
        if(!parentItem){
1831
            parentNode = me.el;
1832
            inWrapper = true;
1833
        }
1834
        else{
1835
            parentNode = me.getNodeFromChildEl(parentItem);
1836
        }
1837

    
1838
        //if the node doesn't exist yet, create it
1839
        if(!node){
1840
            //'<td class="{view.nodeCls}{[!values.node.isLeaf() && !values.node.isExpanded() ? " " + values.view.collapseCls: ""]}" style="padding: 0 {view.nodeSpacing}px;">',
1841
            node = Ext.DomHelper.createDom({
1842
                tag: 'td',
1843
                cls: me.nodeCls + (!record.isLeaf() && !record.isExpanded() ? " " + me.collapseCls : "" ),
1844
                style: "padding: 0 "+me.nodeSpacing+"px",
1845
                html: me.innerNodeTpl.apply({view: me, node: record, nodeId: me.getNodeId(record)})
1846
            });
1847
        }
1848
        else{
1849
            tmp = Ext.DomHelper.createDom({
1850
                tag: 'td',
1851
                cls: me.nodeCls + (!record.isLeaf() && !record.isExpanded() ? " " + me.collapseCls : "" ),
1852
                style: "padding: 0 "+me.nodeSpacing+"px"
1853
            });
1854

    
1855
            //Ext.fly(nodeItem).removeCls('x-item-selected');
1856
            Ext.fly(tmp).appendChild(node.first());
1857
            me.removeNode(node);
1858
            node = tmp;
1859
        }
1860

    
1861
        values = {view: me, node: parentRecord, nodeId: me.getNodeId(parentRecord)};
1862

    
1863

    
1864
        //it´s an append?
1865
        if(!refRecord){
1866
            if(inWrapper){
1867
                //just add to the wrapper container
1868
                container = me.getChildrenContainer(parentNode);
1869
                container.appendChild(node);
1870
            }
1871
            else{
1872
                out = [];
1873

    
1874
                downLine = me.getNodeDownLine(parentNode);
1875

    
1876
                //doesn't have a down line? so it was a leaf before
1877
                if(!downLine){
1878
                    //create full container node parts
1879

    
1880
                    out.push('<table>');
1881
                    out.push(me.downLineTpl.apply(values));
1882
                    out.push(me.childrenLinesTpl.apply(values));
1883
                    out.push(me.containerTpl.apply(values));
1884
                    out.push('</table>');
1885
                    Ext.fly(renderBuffer).setHTML(out.join(''));
1886

    
1887
                    if(!parentRecord.isExpanded()){
1888
                        parentNode.addCls(me.collapseCls);
1889
                    }
1890

    
1891
                    container = me.getChildrenContainer(renderBuffer);
1892
                    body = me.getNodeBody(parentNode);
1893
                    tmp = Ext.fly(renderBuffer).query('tr');
1894
                    len = tmp.length;
1895
                    for(var i= 0; i< len; ++i){
1896
                        body.appendChild(tmp[i]);
1897
                    }
1898

    
1899
                    Ext.fly(container).appendChild(node)
1900

    
1901
                    if(!parentRecord.isExpanded()){
1902
                        parentRecord.expand(false, function(){
1903
                            me.focusNode(me.getItemByRecord(record));
1904
                        }, me)
1905
                    }
1906
                    else me.focusNode(me.getItemByRecord(record));
1907

    
1908
                    return;
1909
                }
1910
                else {
1911
                    //adjust the down line and inline expander
1912
                    Ext.fly(parentItem).parent().set({colSpan: childCount});
1913
                    downLine.set({colSpan: childCount});
1914
                    me.getNodeInlineExpander(parentNode).set({colSpan: childCount});
1915
                }
1916

    
1917
                container = me.getChildrenContainer(parentNode);
1918

    
1919
                //adjust the children lines
1920
                if(childCount > 1){
1921
                    tmp = me.getChildrenLinesContainer( parentNode );
1922
                    if( !tmp ){
1923
                        //was a single child, so children lines doesn't exist yet
1924
                        out = ['<table>']
1925
                        out.push(me.childrenLinesTpl.apply( values ));
1926
                        out.push('</table>')
1927
                        Ext.fly(me.renderBuffer).setHTML(out.join(''));
1928
                        Ext.fly(me.renderBuffer).down('tr').insertBefore(container);
1929
                    }
1930
                    else{
1931
                        //add the new child line
1932
                        var line = Ext.DomHelper.createDom({
1933
                            tag: 'td',
1934
                            html: me.childrenLineTpl.apply(values)
1935
                        });
1936
                        Ext.fly(line).insertAfter(tmp.first());
1937
                    }
1938
                }
1939

    
1940
                //add the new node to children container
1941
                container.appendChild(node);
1942
            }
1943
        }
1944
        else{
1945
            if(!inWrapper){
1946
                Ext.fly(parentItem ).parent().set( {colSpan: childCount} );
1947
                me.getNodeDownLine( parentNode ).set( {colSpan: childCount} );
1948
                me.getNodeInlineExpander( parentNode ).set( {colSpan: childCount} );
1949
            }
1950

    
1951
            container = me.getChildrenContainer(parentNode);
1952

    
1953
            //adjust the children lines
1954
            if(!inWrapper && childCount > 1){
1955
                tmp = me.getChildrenLinesContainer( parentNode );
1956
                if( !tmp ){
1957
                    //was a single child, so children lines doesn't exist yet
1958
                    out = ['<table>']
1959
                    out.push(me.childrenLinesTpl.apply( values ));
1960
                    out.push('</table>')
1961
                    Ext.fly(me.renderBuffer).setHTML(out.join(''));
1962
                    Ext.fly(me.renderBuffer).down('tr').insertBefore(container);
1963
                }
1964
                else{
1965
                    //add the new child line
1966
                    var line = Ext.DomHelper.createDom({
1967
                        tag: 'td',
1968
                        html: me.childrenLineTpl.apply(values)
1969
                    });
1970
                    Ext.fly(line).insertAfter(tmp.first());
1971
                }
1972
            }
1973

    
1974
            reference = me.getItemByRecord(refRecord);
1975
            if(!reference){
1976
                //reference not found so append to container
1977
                container.appendChild(node);
1978
            }
1979
            else{
1980
                reference = me.getNodeFromChildEl(reference);
1981
                Ext.fly(node).insertBefore(reference);
1982
            }
1983

    
1984
        }
1985

    
1986
        if(!parentRecord.isExpanded()){
1987
            parentRecord.expand(false, function(){
1988
                me.focusNode(me.getItemByRecord(record));
1989
            }, me)
1990
        }
1991
        else me.focusNode(me.getItemByRecord(record));
1992

    
1993
    },
1994

    
1995
    onStoreAppend: function(parentRecord, record, index, opt){
1996
        var me = this;
1997

    
1998
        if(me.needNodeJoin) record.join(me.store);
1999

    
2000
        if(me.initialLoad && !me.storeRootLoad){
2001
            //looks like a bug in 4.2.1 and 4.2.2 maybe before too
2002
            if(parentRecord.isLeaf()){
2003
                parentRecord.set('leaf',false);
2004
            }
2005

    
2006
            me.insertNodeFromRecord(record);
2007
        }
2008
    },
2009

    
2010
    onStoreInsert: function(parent, record, refNode, opt){
2011
        if(this.needNodeJoin) record.join(this.store);
2012
        this.insertNodeFromRecord(record, refNode);
2013
    },
2014

    
2015
    onStoreRemove: function(parentRecord, record, isMove, opt){
2016
        var me = this;
2017

    
2018
        //looks like a bug in 4.2.1 and 4.2.2 maybe before too
2019
        if(!parentRecord.isLeaf() && !parentRecord.childNodes.length){
2020
            parentRecord.set('leaf',true);
2021
        }
2022

    
2023
        if(isMove) return;
2024

    
2025
        if(me.needNodeJoin){
2026
            record.unjoin(me.store);
2027
        }
2028

    
2029
        me.removeNodeFromRecord(record);
2030
    },
2031

    
2032
    onStoreBulkRemove: function(store, records, isMove, opt){
2033
        var me = this,
2034
            needJoin = me.needNodeJoin,
2035
            len = records.length,
2036
            record, i;
2037

    
2038
        if(isMove) return;
2039
        me.getSelectionModel().deselect(records);
2040
        for(i=0; i<len; ++i){
2041
            record = records[i];
2042
            if(needJoin){
2043
                record.unjoin(store);
2044
            }
2045

    
2046
            me.removeNodeFromRecord(record);
2047
        }
2048
    },
2049

    
2050
    onStoreUpdate: function(store, record, op, modifiedFields, op){
2051
        var me = this,
2052
            node;
2053
        if(!me.rendered){
2054
            return;
2055
        }
2056

    
2057
        node = me.getItemByRecord(record);
2058
        if(!node) return;
2059

    
2060
        Ext.fly(node).setHTML(me.renderItem(me, record));
2061

    
2062
        me.fireEvent('itemupdate', me, record, node);
2063
    },
2064

    
2065
    onDataRefresh: function(){
2066
        this.refresh();
2067
    },
2068

    
2069
    onStoreClear: function(){
2070
        if(this.rendered){
2071
            this.renderTpl.overwrite(this.el,{});
2072
        }
2073
    },
2074

    
2075
    renderNodes: function(records, out){
2076
        var me = this,
2077
            len = records.length,
2078
            i;
2079

    
2080
        for(i=0; i<len; ++i){
2081
            me.renderNode(records[i], out);
2082
        }
2083
        var elems = document.querySelectorAll('.task-item');
2084
        //console.log(elems);
2085
        //if(elems != [])
2086
        //alert(elems[0].outerHTML);
2087
        //me.onInlineExpanderClick(null, '<span id="ochart-1063-record-root" class="x-ochart-node-content task-item" data-recordid="root">');
2088
    },
2089

    
2090
    renderNode: function(record, out){
2091
        var me = this,
2092
            tpl = me.nodeTpl,
2093
            value = {view: me, node: record, nodeId: me.getNodeId(record)};
2094
        if(out){
2095
            tpl.applyOut(value, out);
2096
        }
2097
        else return tpl.apply(value);
2098
    },
2099

    
2100
    renderItem: function(view, record){
2101
        return view.itemTpl.apply(record.data);
2102
    },
2103

    
2104
    /**
2105
     * Gets the selection model for this component.
2106
     * @return {Ext.selection.Model} The selection model
2107
     */
2108
    getSelectionModel: function(){
2109
        var me = this,
2110
            mode = 'SINGLE';
2111

    
2112
        if (me.simpleSelect) {
2113
            mode = 'SIMPLE';
2114
        }
2115
        /*else if (me.multiSelect) {
2116
            mode = 'MULTI';
2117
        }*/
2118

    
2119
        // No selModel specified, or it's just a config; Instantiate
2120
        if (!me.selModel || !me.selModel.events) {
2121
            me.selModel = new sitools.user.view.modules.datasetExplorerOchart.OChartModel(Ext.apply({
2122
                allowDeselect: me.allowDeselect,
2123
                mode: mode,
2124
                enableKeyNav: true,
2125
                deselectOnContainerClick: true
2126
            }, me.selModel));
2127
        }
2128

    
2129
        if (!me.selModel.hasRelaySetup) {
2130
            me.relayEvents(me.selModel, [
2131
                'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect', 'focuschange'
2132
            ]);
2133
            me.selModel.hasRelaySetup = true;
2134
        }
2135

    
2136
        // lock the selection model if user
2137
        // has disabled selection
2138
        if (me.disableSelection) {
2139
            me.selModel.locked = true;
2140
        }
2141

    
2142
        return me.selModel;
2143
    },
2144

    
2145
    /**
2146
     * Returns true if the passed node is selected, else false.
2147
     * @param {HTMLElement/Number/Ext.data.Model} node The node, node index or record to check
2148
     * @return {Boolean} True if selected, else false
2149
     * @since 2.3.0
2150
     */
2151
    isSelected : function(node) {
2152
        var r = this.getRecord(node);
2153
        return this.selModel.isSelected(r);
2154
    },
2155

    
2156
    /**
2157
     * Handles buffered mouse over and out event
2158
     * @param {HtmlEvent} e The browser event
2159
     *
2160
     * @protected
2161
     */
2162
    onMouseOverOut: function(e) {
2163
        var me = this;
2164

    
2165
        // Determining if we are entering or leaving view items is deferred until
2166
        // mouse move churn settles down.
2167
        me.lastMouseOverOutEvent.setEvent(e.browserEvent, true);
2168
        me.handleMouseOverOrOut(me.lastMouseOverOutEvent);
2169
    },
2170

    
2171
    /**
2172
     * Handles the the mouse over and out events
2173
     *
2174
     * @param {HtmlEvent} e The browser event
2175
     * @protected
2176
     */
2177
    handleMouseOverOrOut: function(e) {
2178
        var me = this,
2179
            isMouseout = e.type === 'mouseout',
2180
            method = isMouseout ? e.getRelatedTarget : e.getTarget,
2181
            nowOverItem = method.call(e, me.itemSelector);
2182

    
2183
        // If the mouse event of whatever type tells use that we are no longer over the current mouseOverItem...
2184
        if (!me.mouseOverItem || nowOverItem !== me.mouseOverItem) {
2185

    
2186
            // First fire mouseleave for the item we just left
2187
            if (me.mouseOverItem) {
2188
                e.item = me.mouseOverItem;
2189
                e.newType = 'mouseleave';
2190
                me.handleEvent(e);
2191
            }
2192

    
2193
            // If we are over an item, fire the mouseenter
2194
            me.mouseOverItem = nowOverItem;
2195
            if (me.mouseOverItem) {
2196
                e.item = me.mouseOverItem;
2197
                e.newType = 'mouseenter';
2198
                me.handleEvent(e);
2199
            }
2200
            else{
2201
                e.item = null;
2202
                me.handleEvent(e);
2203
            }
2204
        }
2205
        else  me.handleEvent(e);
2206
    },
2207

    
2208
    /**
2209
     * Handle browser events and internal events
2210
     *
2211
     * @param e The event
2212
     *
2213
     * @protected
2214
     */
2215
    handleEvent: function(e) {
2216
        var me = this,
2217
            key = e.type == 'keydown' && e.getKey();
2218

    
2219
        me.processUIEvent(e);
2220

    
2221
        // After all listeners have processed the event, then unless the user is typing into an input field,
2222
        // prevent browser's default action on SPACE which is to focus the event's target element.
2223
        // Focusing causes the browser to attempt to scroll the element into view.
2224
        if (key === e.SPACE) {
2225
            if (!me.inputTagRe.test(e.getTarget().tagName)) {
2226
                e.stopEvent();
2227
            }
2228
        }
2229
    },
2230

    
2231
    /**
2232
     * Try to process an UI event
2233
     *
2234
     * @param e
2235
     *
2236
     * @returns {Boolean} true if the event was processed
2237
     *
2238
     * @protected
2239
     */
2240
    processUIEvent: function(e) {
2241
        // If the target event has been removed from the body (data update causing view DOM to be updated),
2242
        // do not process. isAncestor uses native methods to check.
2243
        if (!Ext.getBody().isAncestor(e.target)) {
2244
            return;
2245
        }
2246

    
2247
        var me = this,
2248
            item = e.getTarget(me.itemSelector),
2249
            map = this.statics().EventMap,
2250
            index, record,
2251
            type = e.type,
2252
            newType = e.type,
2253
            sm;
2254

    
2255

    
2256
        // If the event is a mouseover/mouseout event converted to a mouseenter/mouseleave,
2257
        // use that event type and ensure that the item is correct.
2258
        if (e.newType) {
2259
            newType = e.newType;
2260
            item = e.item;
2261
        }
2262

    
2263
        // For keydown events, try to get either the last focused item or the selected item.
2264
        // If we have not focused an item, we'll just fire a container keydown event.
2265
        if (!item && type == 'keydown') {
2266
            sm = me.getSelectionModel();
2267
            record = sm.lastFocused || sm.getLastSelected();
2268
            if (record) {
2269
                item = me.getNode(record, true);
2270
            }
2271
        }
2272

    
2273
        //node item event?
2274
        if (item) {
2275
            //get the record for the template element
2276
            if (!record) {
2277
                record = me.getRecord(item);
2278
            }
2279

    
2280
            // It is possible for an event to arrive for which there is no record... this
2281
            // can happen with dblclick where the clicks are on removal actions (think a
2282
            // grid w/"delete row" action column)
2283
            if (!record) {
2284
                return false;
2285
            }
2286

    
2287
            index = record.get('index');
2288

    
2289
            //call event handlers
2290
            if((me['onBeforeItem' + map[newType]](record, item, index, e) === false) ||
2291
                (me.fireEvent('beforeitem' + newType, me, record, item, index, e) === false) ||
2292
                (me['onItem' + map[newType]](record, item, index, e) === false)
2293
                ) {
2294
                return false;
2295
            }
2296

    
2297
            me.fireEvent('item' + newType, me, record, item, index, e);
2298
        }
2299
        else {
2300
            item = e.getTarget(me.expanderSelector, 1);
2301
            if(item) return false;
2302
            //container event handler
2303
            if((me.processContainerEvent(e) === false) ||
2304
                (me['onBeforeContainer' + map[type]](e) === false) ||
2305
                (me.fireEvent('beforecontainer' + type, me, e) === false) ||
2306
                (me['onContainer' + map[type]](e) === false)
2307
                ) {
2308
                return false;
2309
            }
2310

    
2311
            me.fireEvent('container' + type, me, e);
2312
        }
2313

    
2314
        return true;
2315
    },
2316

    
2317
    // @private
2318
    setHighlightedItem: function(item){
2319
        var me = this,
2320
            highlighted = me.highlightedItem,
2321
            overItemCls = me.overItemCls,
2322
            beforeOverItemCls = me.beforeOverItemCls,
2323
            previous;
2324

    
2325
        if (highlighted != item){
2326
            if (highlighted) {
2327
                Ext.fly(highlighted).removeCls(overItemCls);
2328
                previous = highlighted.previousSibling;
2329
                if (beforeOverItemCls && previous) {
2330
                    Ext.fly(previous).removeCls(beforeOverItemCls);
2331
                }
2332
                me.fireEvent('unhighlightitem', me, highlighted);
2333
            }
2334

    
2335
            me.highlightedItem = item;
2336

    
2337
            if (item) {
2338
                Ext.fly(item).addCls(me.overItemCls);
2339
                previous = item.previousSibling;
2340
                if (beforeOverItemCls && previous) {
2341
                    Ext.fly(previous).addCls(beforeOverItemCls);
2342
                }
2343
                me.fireEvent('highlightitem', me, item);
2344
            }
2345
        }
2346
    },
2347

    
2348
    /**
2349
     * Highlights a given item in the View. This is called by the mouseover handler if {@link #overItemCls}
2350
     * and {@link #trackOver} are configured, but can also be called manually by other code, for instance to
2351
     * handle stepping through the list via keyboard navigation.
2352
     * @param {HTMLElement} item The item to highlight
2353
     */
2354
    highlightItem: function(item) {
2355
        this.setHighlightedItem(item);
2356
    },
2357

    
2358
    /**
2359
     * Un-highlights the currently highlighted item, if any.
2360
     */
2361
    clearHighlight: function() {
2362
        this.setHighlightedItem(undefined);
2363
    },
2364

    
2365
    // protected
2366
    onItemMouseEnter: function(record, item, index, e) {
2367
        var me = this,
2368
            h;
2369

    
2370
        if(me.panning) return;
2371

    
2372
        if (this.trackOver) {
2373
            this.highlightItem(item);
2374
        }
2375

    
2376
        if(me.toolsVisible && !me.dragging){
2377
            if( !record.isLeaf() && record.isExpanded() && !record.isRoot() ){
2378
                me.expandTool.show();
2379
                me.expandTool.alignTo( item, 'bl-tl' );
2380
            }
2381

    
2382
            if( !this.readOnly ){
2383
                if(!record.isRoot()){
2384
                    me.addBeforeTool.show();
2385
                    me.addBeforeTool.alignTo( item, 'r-l' );
2386
                    me.addAfterTool.show();
2387
                    me.addAfterTool.alignTo( item, 'l-r' );
2388
                    me.removeItemTool.show();
2389
                    me.removeItemTool.alignTo( item, 'br-tr' );
2390
                }
2391
                me.addChildTool.show();
2392
                me.addChildTool.alignTo( item, 't-b' );
2393
            }
2394
        }
2395

    
2396
        me.lastOverItem = item;
2397
    },
2398

    
2399
    // protected
2400
    onItemMouseLeave : function(record, item, index, e) {
2401
        if (this.trackOver) {
2402
            this.clearHighlight();
2403
        }
2404
    },
2405

    
2406
    // protected, template methods
2407
    processContainerEvent: Ext.emptyFn,
2408
    processItemEvent: Ext.emptyFn,
2409

    
2410
    onBeforeItemMouseOut: Ext.emptyFn,
2411
    onBeforeItemMouseOver: Ext.emptyFn,
2412
    onItemMouseOut: Ext.emptyFn,
2413
    onItemMouseOver: Ext.emptyFn,
2414

    
2415
    onItemMouseDown: Ext.emptyFn,
2416
    onItemMouseUp: Ext.emptyFn,
2417
    onItemFocus: Ext.emptyFn,
2418
    onItemClick: function(record){
2419
        if(Ext.versions.extjs.isLessThan( '4.2.0' )){
2420
            this.focusNode(record);
2421
        }
2422
    },
2423
    onItemDblClick: Ext.emptyFn,
2424
    onItemContextMenu: Ext.emptyFn,
2425
    onItemKeyDown: Ext.emptyFn,
2426
    onBeforeItemMouseDown: Ext.emptyFn,
2427
    onBeforeItemMouseUp: Ext.emptyFn,
2428
    onBeforeItemFocus: Ext.emptyFn,
2429
    onBeforeItemMouseEnter: Ext.emptyFn,
2430
    onBeforeItemMouseLeave: Ext.emptyFn,
2431
    onBeforeItemClick: Ext.emptyFn,
2432
    onBeforeItemDblClick: Ext.emptyFn,
2433
    onBeforeItemContextMenu: Ext.emptyFn,
2434
    onBeforeItemKeyDown: Ext.emptyFn,
2435

    
2436
    onExpanderMouseOver: Ext.emptyFn,
2437
    onExpanderMouseOut: Ext.emptyFn,
2438

    
2439
    onInlineExpanderClick: function(e, t){
2440
        console.log(e);
2441
        console.log(t);
2442
        var me = this,
2443
            node = me.getNodeFromChildEl(t),
2444
            record,
2445
            item,
2446
            nodeRegion;
2447

    
2448
        if(!node) return;
2449

    
2450
        item = node.down(me.itemSelector);
2451
        record = me.getRecord(item);
2452
        if(!record) return;
2453
        console.log(record);
2454
        if(record.isExpanded()) me.collapse(record);
2455
        else me.expand(record);
2456
    },
2457

    
2458
    onItemExpandClick: function(){
2459
        var me = this;
2460
        me.onInlineExpanderClick(null, me.lastOverItem);
2461
        me.hideTools();
2462
    },
2463

    
2464
    hideTools: function(){
2465
        var me = this;
2466
        if(me.toolsVisible && me.expandTool){
2467
            me.expandTool.hide();
2468
            me.addBeforeTool.hide();
2469
            me.addAfterTool.hide();
2470
            me.addChildTool.hide();
2471
            me.removeItemTool.hide();
2472
            me.lastOverItem = null;
2473
        }
2474
    },
2475

    
2476
    fireAddItem: function(item, where){
2477
        var me = this,
2478
            record;
2479
        if(!item) return;
2480

    
2481
        record = me.getRecord(item)
2482

    
2483
        me.fireEvent('additem', me, record, where, item);
2484
        me.hideTools();
2485
    },
2486

    
2487
    onItemAddBeforeClick: function(){
2488
        var item = this.lastOverItem;
2489
        if(!item) return;
2490
        this.fireAddItem(item, 'before');
2491
    },
2492

    
2493
    onItemAddAfterClick: function(){
2494
        var item = this.lastOverItem;
2495
        if(!item) return;
2496
        this.fireAddItem(item, 'after');
2497
    },
2498

    
2499
    onItemAddChildClick: function(){
2500
        var item = this.lastOverItem;
2501
        if(!item) return;
2502
        this.fireAddItem(item, 'child');
2503
    },
2504

    
2505
    onItemRemoveClick: function(){
2506
        var me = this,
2507
            item = this.lastOverItem,
2508
            record;
2509

    
2510
        if(!item) return;
2511

    
2512
        record = me.getRecord(item)
2513

    
2514
        me.fireEvent('removeitem', me, record, item);
2515
        me.hideTools();
2516
    },
2517

    
2518
    onPanning: function(e) {
2519
        var me = this;
2520
        e.stopEvent();
2521

    
2522
        var x = e.getPageX(),
2523
            y = e.getPageY(),
2524
            xDelta = x - me.mouseX,
2525
            yDelta = y - me.mouseY;
2526

    
2527
        me.scrollBy(-xDelta, -yDelta);
2528
        me.mouseX = x;
2529
        me.mouseY = y;
2530
    },
2531

    
2532
    onFinishPanning: function(e) {
2533
        var me = this;
2534
        Ext.getBody().un('mousemove', me.onPanning, me);
2535
        Ext.getDoc().un('mouseup', me.onFinishPanning, me);
2536

    
2537
        if (Ext.isIE || Ext.isGecko) {
2538
            Ext.getBody().un('mouseenter', me.onFinishPanning, me);
2539
        }
2540

    
2541
        me.el.setStyle('cursor','default');
2542

    
2543
        me.panning = false;
2544
    },
2545

    
2546
    onContainerMouseDown: function(e){
2547
        var me = this;
2548
        me.mouseX = e.getPageX();
2549
        me.mouseY = e.getPageY();
2550
        Ext.getBody().on('mousemove', me.onPanning, me);
2551
        Ext.getDoc().on('mouseup', me.onFinishPanning, me);
2552

    
2553
        // For IE (and FF if using frames), if you move mouse onto the browser chrome and release mouse button
2554
        // we won't know about it. Next time mouse enters the body, cancel any ongoing pan activity as a fallback.
2555
        if (Ext.isIE || Ext.isGecko) {
2556
            Ext.getBody().on('mouseenter', me.onFinishPanning, me);
2557
        }
2558

    
2559
        me.el.setStyle('cursor','move');
2560

    
2561
        me.panning = true;
2562

    
2563
        // required for some weird chrome bug/behavior, when whole panel was scrolled-out
2564
        e.stopEvent();
2565
    },
2566

    
2567
    onContainerMouseUp: Ext.emptyFn,
2568

    
2569
    onContainerMouseOver: function(){
2570
        this.hideTools();
2571
    },
2572
    onContainerMouseOut: Ext.emptyFn,
2573
    onContainerClick: function(){
2574
        this.el.focus();
2575
    },
2576
    onContainerDblClick: Ext.emptyFn,
2577
    onContainerContextMenu: Ext.emptyFn,
2578
    onContainerKeyDown: Ext.emptyFn,
2579
    onBeforeContainerMouseDown: Ext.emptyFn,
2580
    onBeforeContainerMouseUp: Ext.emptyFn,
2581
    onBeforeContainerMouseOver: Ext.emptyFn,
2582
    onBeforeContainerMouseOut: Ext.emptyFn,
2583
    onBeforeContainerClick: Ext.emptyFn,
2584
    onBeforeContainerDblClick: Ext.emptyFn,
2585
    onBeforeContainerContextMenu: Ext.emptyFn,
2586
    onBeforeContainerKeyDown: Ext.emptyFn,
2587

    
2588
    // invoked by the selection model to maintain visual UI cues
2589
    onItemSelect: function(record) {
2590
        var node = this.getNode(record);
2591

    
2592
        if(node) {
2593
            Ext.fly(node).addCls(this.selectedItemCls);
2594
        }
2595
    },
2596

    
2597
    // invoked by the selection model to maintain visual UI cues
2598
    onItemDeselect: function(record) {
2599
        var node = this.getNode(record);
2600

    
2601
        if(node) {
2602
            Ext.fly(node).removeCls(this.selectedItemCls);
2603
        }
2604
    },
2605

    
2606
    /**
2607
     * Gets the item CSS selector (node content)
2608
     *
2609
     * This is required by the selection model
2610
     * @returns {String} The CSS selector
2611
     */
2612
    getItemSelector: function() {
2613
        return this.itemSelector;
2614
    },
2615

    
2616
    /**
2617
     * Focuses a node in the view.
2618
     * @param {Ext.data.Model} rec The record associated to the node that is to be focused.
2619
     */
2620
    focusNode: function(rec){
2621
        var me          = this,
2622
            //todo verificar se deve trazer o container do nó
2623
            //node        = me.getNode(rec, true),
2624
            node        = me.getNode(rec),
2625
            el          = me.el,
2626
            adjustmentY = 0,
2627
            adjustmentX = 0,
2628
            elRegion    = el.getRegion(),
2629
            nodeRegion;
2630

    
2631
        // Viewable region must not include scrollbars, so use
2632
        // DOM client dimensions
2633
        elRegion.bottom = elRegion.top + el.dom.clientHeight;
2634
        elRegion.right = elRegion.left + el.dom.clientWidth;
2635
        if (node) {
2636
            nodeRegion = Ext.fly(node).getRegion();
2637
            // node is above
2638
            if (nodeRegion.top < elRegion.top) {
2639
                adjustmentY = nodeRegion.top - elRegion.top;
2640
                // node is below
2641
            } else if (nodeRegion.bottom > elRegion.bottom) {
2642
                adjustmentY = nodeRegion.bottom - elRegion.bottom;
2643
            }
2644

    
2645
            // node is left
2646
            if (nodeRegion.left < elRegion.left) {
2647
                adjustmentX = nodeRegion.left - elRegion.left;
2648
                // node is right
2649
            } else if (nodeRegion.right > elRegion.right) {
2650
                adjustmentX = nodeRegion.right - elRegion.right;
2651
            }
2652

    
2653
            if (adjustmentX || adjustmentY) {
2654
                me.scrollBy(adjustmentX, adjustmentY, false);
2655
            }
2656
            el.focus();
2657
        }
2658
    },
2659

    
2660
    startDrag: function(){
2661
        this.dragging = true;
2662
    },
2663

    
2664
    endDrag: function(){
2665
        this.dragging = false;
2666
    }
2667
});
2668