1// Copyright (c) 2011 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
9cr.define('cr.ui.table', function() {
10  const 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    },
35
36    /**
37     * Updates table header width. Header width depends on list having a
38     * vertical scrollbar.
39     */
40    updateWidth: function() {
41      // Header should not span over the vertical scrollbar of the list.
42      var list = this.table_.querySelector('list');
43      this.headerInner_.style.width = list.clientWidth + 'px';
44    },
45
46    /**
47     * Resizes columns.
48     */
49    resize: function() {
50      var cm = this.table_.columnModel;
51
52      var headerCells = this.querySelectorAll('.table-header-cell');
53      if (headerCells.length != cm.size) {
54        this.redraw();
55        return;
56      }
57      this.headerInner_.textContent = '';
58      for (var i = 0; i < cm.size; i++) {
59        headerCells[i].style.width = cm.getWidth(i) + '%';
60        this.headerInner_.appendChild(headerCells[i]);
61      }
62      this.appendSplitters_();
63    },
64
65    /**
66     * Redraws table header.
67     */
68    redraw: function() {
69      var cm = this.table_.columnModel;
70      var dm = this.table_.dataModel;
71
72      this.updateWidth();
73      this.headerInner_.textContent = '';
74
75      if (!cm || ! dm) {
76        return;
77      }
78
79      for (var i = 0; i < cm.size; i++) {
80        var cell = this.ownerDocument.createElement('div');
81        cell.style.width = cm.getWidth(i) + '%';
82        cell.className = 'table-header-cell';
83        cell.addEventListener('click', this.createSortFunction_(i).bind(this));
84
85        cell.appendChild(this.createHeaderLabel_(i));
86        this.headerInner_.appendChild(cell);
87      }
88      this.appendSplitters_();
89    },
90
91    /**
92     * Appends column splitters to the table header.
93     */
94    appendSplitters_: function() {
95      var cm = this.table_.columnModel;
96
97      var leftPercent = 0;
98      for (var i = 0; i < cm.size - 1; i++) {
99        leftPercent += cm.getWidth(i);
100
101        // splitter should use CSS for background image.
102        var splitter = new TableSplitter({table: this.table_});
103        splitter.columnIndex = i;
104
105        var rtl = this.ownerDocument.defaultView.getComputedStyle(this).
106            direction == 'rtl';
107        splitter.style.left = rtl ? 100 - leftPercent + '%' : leftPercent + '%';
108
109        this.headerInner_.appendChild(splitter);
110      }
111    },
112
113    /**
114     * Renders column header. Appends text label and sort arrow if needed.
115     * @param {number} index Column index.
116     */
117    createHeaderLabel_: function(index) {
118      var cm = this.table_.columnModel;
119      var dm = this.table_.dataModel;
120
121      var labelDiv = this.ownerDocument.createElement('div');
122      labelDiv.className = 'table-header-label';
123
124      var span = this.ownerDocument.createElement('span');
125      span.textContent = cm.getName(index);
126      var rtl = this.ownerDocument.defaultView.getComputedStyle(this).
127          direction == 'rtl';
128      if (rtl) {
129        span.style.backgroundPosition = 'left';
130        span.style.paddingRight= '0';
131      } else {
132        span.style.backgroundPosition = 'right';
133        span.style.paddingLeft= '0';
134      }
135      if (dm) {
136        if (dm.sortStatus.field == cm.getId(index)) {
137          if (dm.sortStatus.direction == 'desc')
138            span.className = 'table-header-sort-image-desc';
139          else
140            span.className = 'table-header-sort-image-asc';
141        }
142      }
143      labelDiv.appendChild(span);
144      return labelDiv;
145    },
146
147    /**
148     * Creates sort function for given column.
149     * @param {number} index The index of the column to sort by.
150     */
151    createSortFunction_: function(index) {
152      return function() {
153        this.table_.sort(index);
154      }.bind(this);
155    },
156  };
157
158  /**
159   * The table associated with the header.
160   * @type {cr.ui.Table}
161   */
162  cr.defineProperty(TableHeader, 'table');
163
164  return {
165    TableHeader: TableHeader
166  };
167});
168