Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / webui / resources / js / cr / ui / table / table_header.js
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6  * @fileoverview This implements a table header.
7  */
8
9 cr.define('cr.ui.table', function() {
10   /** @const */ var TableSplitter = cr.ui.TableSplitter;
11
12   /**
13    * Creates a new table header.
14    * @param {Object=} opt_propertyBag Optional properties.
15    * @constructor
16    * @extends {HTMLDivElement}
17    */
18   var TableHeader = cr.ui.define('div');
19
20   TableHeader.prototype = {
21     __proto__: HTMLDivElement.prototype,
22
23     table_: null,
24
25     /**
26      * Initializes the element.
27      */
28     decorate: function() {
29       this.className = 'table-header';
30
31       this.headerInner_ = this.ownerDocument.createElement('div');
32       this.headerInner_.className = 'table-header-inner';
33       this.appendChild(this.headerInner_);
34       this.addEventListener('touchstart',
35           this.handleTouchStart_.bind(this), false);
36     },
37
38     /**
39      * Updates table header width. Header width depends on list having a
40      * vertical scrollbar.
41      */
42     updateWidth: function() {
43       // Header should not span over the vertical scrollbar of the list.
44       var list = this.table_.querySelector('list');
45       this.headerInner_.style.width = list.clientWidth + 'px';
46     },
47
48     /**
49      * Resizes columns.
50      */
51     resize: function() {
52       var cm = this.table_.columnModel;
53
54       var headerCells = this.querySelectorAll('.table-header-cell');
55       if (headerCells.length != cm.size) {
56         this.redraw();
57         return;
58       }
59
60       for (var i = 0; i < cm.size; i++) {
61         headerCells[i].style.width = cm.getWidth(i) + 'px';
62       }
63       this.placeSplitters_(this.querySelectorAll('.table-header-splitter'));
64     },
65
66     batchCount_: 0,
67
68     startBatchUpdates: function() {
69       this.batchCount_++;
70     },
71
72     endBatchUpdates: function() {
73       this.batchCount_--;
74       if (this.batchCount_ == 0)
75         this.redraw();
76     },
77
78     /**
79      * Redraws table header.
80      */
81     redraw: function() {
82       if (this.batchCount_ != 0)
83         return;
84
85       var cm = this.table_.columnModel;
86       var dm = this.table_.dataModel;
87
88       this.updateWidth();
89       this.headerInner_.textContent = '';
90
91       if (!cm || ! dm) {
92         return;
93       }
94
95       for (var i = 0; i < cm.size; i++) {
96         var cell = this.ownerDocument.createElement('div');
97         cell.style.width = cm.getWidth(i) + 'px';
98         cell.className = 'table-header-cell';
99         if (dm.isSortable(cm.getId(i)))
100           cell.addEventListener('click',
101                                 this.createSortFunction_(i).bind(this));
102
103         cell.appendChild(this.createHeaderLabel_(i));
104         this.headerInner_.appendChild(cell);
105       }
106       this.appendSplitters_();
107     },
108
109     /**
110      * Appends column splitters to the table header.
111      */
112     appendSplitters_: function() {
113       var cm = this.table_.columnModel;
114       var splitters = [];
115       for (var i = 0; i < cm.size; i++) {
116         // splitter should use CSS for background image.
117         var splitter = new TableSplitter({table: this.table_});
118         splitter.columnIndex = i;
119         splitter.addEventListener('dblclick',
120                               this.handleDblClick_.bind(this, i));
121
122         this.headerInner_.appendChild(splitter);
123         splitters.push(splitter);
124       }
125       this.placeSplitters_(splitters);
126     },
127
128     /**
129      * Place splitters to right positions.
130      * @param {Array.<HTMLElement>|NodeList} splitters Array of splitters.
131      */
132     placeSplitters_: function(splitters) {
133       var cm = this.table_.columnModel;
134       var place = 0;
135       for (var i = 0; i < cm.size; i++) {
136         place += cm.getWidth(i);
137         splitters[i].style.webkitMarginStart = place + 'px';
138       }
139     },
140
141     /**
142      * Renders column header. Appends text label and sort arrow if needed.
143      * @param {number} index Column index.
144      */
145     createHeaderLabel_: function(index) {
146       var cm = this.table_.columnModel;
147       var dm = this.table_.dataModel;
148
149       var labelDiv = this.ownerDocument.createElement('div');
150       labelDiv.className = 'table-header-label';
151
152       if (cm.isEndAlign(index))
153         labelDiv.style.textAlign = 'end';
154       var span = this.ownerDocument.createElement('span');
155       span.appendChild(cm.renderHeader(index, this.table_));
156       span.style.padding = '0';
157
158       if (dm) {
159         if (dm.sortStatus.field == cm.getId(index)) {
160           if (dm.sortStatus.direction == 'desc')
161             span.className = 'table-header-sort-image-desc';
162           else
163             span.className = 'table-header-sort-image-asc';
164         }
165       }
166       labelDiv.appendChild(span);
167       return labelDiv;
168     },
169
170     /**
171      * Creates sort function for given column.
172      * @param {number} index The index of the column to sort by.
173      */
174     createSortFunction_: function(index) {
175       return function() {
176         this.table_.sort(index);
177       }.bind(this);
178     },
179
180     /**
181      * Handles the touchstart event. If the touch happened close enough
182      * to a splitter starts dragging.
183      * @param {Event} e The touch event.
184      */
185     handleTouchStart_: function(e) {
186       e = /** @type {TouchEvent} */ (e);
187       if (e.touches.length != 1)
188         return;
189       var clientX = e.touches[0].clientX;
190
191       var minDistance = TableHeader.TOUCH_DRAG_AREA_WIDTH;
192       var candidate;
193
194       var splitters = this.querySelectorAll('.table-header-splitter');
195       for (var i = 0; i < splitters.length; i++) {
196         var r = splitters[i].getBoundingClientRect();
197         if (clientX <= r.left && r.left - clientX <= minDistance) {
198           minDistance = r.left - clientX;
199           candidate = splitters[i];
200         }
201         if (clientX >= r.right && clientX - r.right <= minDistance) {
202           minDistance = clientX - r.right;
203           candidate = splitters[i];
204         }
205       }
206       if (candidate)
207         candidate.startDrag(clientX, true);
208       // Splitter itself shouldn't handle this event.
209       e.stopPropagation();
210     },
211
212     /**
213      * Handles the double click on a column separator event.
214      * Ajusts column width.
215      * @param {number} index Column index.
216      * @param {Event} e The double click event.
217      */
218     handleDblClick_: function(index, e) {
219      this.table_.fitColumn(index);
220     }
221   };
222
223   /**
224    * The table associated with the header.
225    * @type {cr.ui.Table}
226    */
227   cr.defineProperty(TableHeader, 'table');
228
229   /**
230    * Rectangular area around the splitters sensitive to touch events
231    * (in pixels).
232    */
233   TableHeader.TOUCH_DRAG_AREA_WIDTH = 30;
234
235   return {
236     TableHeader: TableHeader
237   };
238 });