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'use strict';
6
7/**
8 * @fileoverview Simple list view.
9 */
10base.requireStylesheet('ui.list_view');
11
12base.require('base.events');
13base.require('ui');
14base.require('ui.container_that_decorates_its_children');
15
16base.exportTo('ui', function() {
17  /**
18   * @constructor
19   */
20  var ListView = ui.define('x-list-view', ui.ContainerThatDecoratesItsChildren);
21
22  ListView.prototype = {
23    __proto__: ui.ContainerThatDecoratesItsChildren.prototype,
24
25    decorate: function() {
26      ui.ContainerThatDecoratesItsChildren.prototype.decorate.call(this);
27
28      this.classList.add('x-list-view');
29      this.onItemClicked_ = this.onItemClicked_.bind(this);
30      this.onKeyDown_ = this.onKeyDown_.bind(this);
31      this.tabIndex = 0;
32      this.addEventListener('keydown', this.onKeyDown_);
33
34      this.selectionChanged_ = false;
35    },
36
37    decorateChild_: function(item) {
38      item.classList.add('list-item');
39      item.addEventListener('click', this.onItemClicked_, true);
40
41      var listView = this;
42      Object.defineProperty(
43          item,
44          'selected', {
45            configurable: true,
46            set: function(value) {
47              var oldSelection = listView.selectedElement;
48              if (oldSelection && oldSelection != this && value)
49                listView.selectedElement.removeAttribute('selected');
50              if (value)
51                this.setAttribute('selected', 'selected');
52              else
53                this.removeAttribute('selected');
54              var newSelection = listView.selectedElement;
55              if (newSelection != oldSelection)
56                base.dispatchSimpleEvent(listView, 'selection-changed', false);
57            },
58            get: function() {
59              return this.hasAttribute('selected');
60            }
61          });
62    },
63
64    undecorateChild_: function(item) {
65      this.selectionChanged_ |= item.selected;
66
67      item.classList.remove('list-item');
68      item.removeEventListener('click', this.onItemClicked_);
69      delete item.selected;
70    },
71
72    beginDecorating_: function() {
73      this.selectionChanged_ = false;
74    },
75
76    doneDecoratingForNow_: function() {
77      if (this.selectionChanged_)
78        base.dispatchSimpleEvent(this, 'selection-changed', false);
79    },
80
81    get selectedElement() {
82      var el = this.querySelector('.list-item[selected]');
83      if (!el)
84        return undefined;
85      return el;
86    },
87
88    set selectedElement(el) {
89      if (!el) {
90        if (this.selectedElement)
91          this.selectedElement.selected = false;
92        return;
93      }
94
95      if (el.parentElement != this)
96        throw new Error(
97            'Can only select elements that are children of this list view');
98      el.selected = true;
99    },
100
101    clear: function() {
102      var changed = this.selectedElement !== undefined;
103      ui.ContainerThatDecoratesItsChildren.prototype.clear.call(this);
104      if (changed)
105        base.dispatchSimpleEvent(this, 'selection-changed', false);
106    },
107
108    onItemClicked_: function(e) {
109      var currentSelectedElement = this.selectedElement;
110      if (currentSelectedElement)
111        currentSelectedElement.removeAttribute('selected');
112      var element = e.target;
113      while (element.parentElement != this)
114        element = element.parentElement;
115      element.setAttribute('selected', 'selected');
116      base.dispatchSimpleEvent(this, 'selection-changed', false);
117    },
118
119    onKeyDown_: function(e) {
120      if (e.keyCode == 38) { // Up arrow.
121        var prev = this.selectedElement.previousSibling;
122        if (prev) {
123          prev.selected = true;
124          prev.scrollIntoView(false);
125          e.preventDefault();
126          return true;
127        }
128      } else if (e.keyCode == 40) { // Down arrow.
129        var next = this.selectedElement.nextSibling;
130        if (next) {
131          next.selected = true;
132          next.scrollIntoView(false);
133          e.preventDefault();
134          return true;
135        }
136      }
137    },
138
139    addItem: function(textContent) {
140      var item = document.createElement('div');
141      item.textContent = textContent;
142      this.appendChild(item);
143      return item;
144    }
145
146  };
147
148  return {
149    ListView: ListView
150  };
151
152});
153