- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / examples / extensions / plugin_settings / js / plugin_list.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 Defines a list of plug-ins that shows for each plug-in a list
7  * of content setting rules.
8  */
9
10 cr.define('pluginSettings.ui', function() {
11   const List = cr.ui.List;
12   const ListItem = cr.ui.ListItem;
13   const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
14
15   /**
16    * CSS classes used by this class.
17    * @enum {string}
18    */
19   const CSSClass = {
20
21     /**
22      * Hides an element.
23      */
24     HIDDEN: 'hidden',
25
26     /**
27      * A plug-in list.
28      */
29     PLUGIN_LIST: 'plugin-list',
30
31     /**
32      * Set on a plug-in list entry to show details about the plug-in.
33      */
34     PLUGIN_SHOW_DETAILS: 'plugin-show-details',
35
36     /**
37      * The plug-in name.
38      */
39     PLUGIN_NAME: 'plugin-name',
40
41     /**
42      * The number of rules set for a plug-in.
43      */
44     NUM_RULES: 'num-rules',
45
46     /**
47      * The element containing details about a plug-in.
48      */
49     PLUGIN_DETAILS: 'plugin-details',
50
51     /**
52      * The element containing the column headers for the list of rules.
53      */
54     COLUMN_HEADERS: 'column-headers',
55
56     /**
57      * The header for the pattern column.
58      */
59     PATTERN_COLUMN_HEADER: 'pattern-column-header',
60
61     /**
62      * The header for the setting column.
63      */
64     SETTING_COLUMN_HEADER: 'setting-column-header',
65   };
66
67   /**
68    * Returns the item's height, like offsetHeight but such that it works better
69    * when the page is zoomed. See the similar calculation in @{code cr.ui.List}.
70    * This version also accounts for the animation done in this file.
71    * @param {!Element} item The item to get the height of.
72    * @return {number} The height of the item, calculated with zooming in mind.
73    */
74   function getItemHeight(item) {
75     var height = item.style.height;
76     // Use the fixed animation target height if set, in case the element is
77     // currently being animated and we'd get an intermediate height below.
78     if (height && height.substr(-2) == 'px') {
79       return parseInt(height.substr(0, height.length - 2));
80     }
81     return item.getBoundingClientRect().height;
82   }
83
84   /**
85    * Creates a new plug-in list item element.
86    * @param {!PluginList} list The plug-in list containing this item.
87    * @param {!Object} info Information about the plug-in.
88    * @constructor
89    * @extends {cr.ui.ListItem}
90    */
91   function PluginListItem(list, info) {
92     var el = cr.doc.createElement('li');
93
94     /**
95      * The plug-in list containing this item.
96      * @type {!PluginList}
97      * @private
98      */
99     el.list_ = list;
100
101     /**
102      * Information about the plug-in.
103      * @type {!Object}
104      * @private
105      */
106     el.info_ = info;
107
108     el.__proto__ = PluginListItem.prototype;
109     el.decorate();
110     return el;
111   }
112
113   PluginListItem.prototype = {
114     __proto__: ListItem.prototype,
115
116     /**
117      * The element containing details about the plug-in. This is only null in
118      * the prototype.
119      * @type {?HTMLDivElement}
120      * @private
121      */
122     detailsElement_: null,
123
124     /**
125      * Initializes the element.
126      */
127     decorate: function() {
128       ListItem.prototype.decorate.call(this);
129
130       var info = this.info_;
131
132       var contentElement = this.ownerDocument.createElement('div');
133
134       var titleEl = this.ownerDocument.createElement('div');
135       var nameEl = this.ownerDocument.createElement('span');
136       nameEl.className = CSSClass.PLUGIN_NAME;
137       nameEl.textContent = info.description;
138       nameEl.title = info.description;
139       titleEl.appendChild(nameEl);
140       this.numRulesEl_ = this.ownerDocument.createElement('span');
141       this.numRulesEl_.className = CSSClass.NUM_RULES;
142       titleEl.appendChild(this.numRulesEl_);
143       contentElement.appendChild(titleEl);
144
145       this.detailsElement_ = this.ownerDocument.createElement('div');
146       this.detailsElement_.classList.add(CSSClass.PLUGIN_DETAILS);
147       this.detailsElement_.classList.add(CSSClass.HIDDEN);
148
149       var columnHeadersEl = this.ownerDocument.createElement('div');
150       columnHeadersEl.className = CSSClass.COLUMN_HEADERS;
151       var patternColumnEl = this.ownerDocument.createElement('div');
152       patternColumnEl.textContent =
153           chrome.i18n.getMessage('patternColumnHeader');
154       patternColumnEl.className = CSSClass.PATTERN_COLUMN_HEADER;
155       var settingColumnEl = this.ownerDocument.createElement('div');
156       settingColumnEl.textContent =
157           chrome.i18n.getMessage('settingColumnHeader');
158       settingColumnEl.className = CSSClass.SETTING_COLUMN_HEADER;
159       columnHeadersEl.appendChild(patternColumnEl);
160       columnHeadersEl.appendChild(settingColumnEl);
161       this.detailsElement_.appendChild(columnHeadersEl);
162       contentElement.appendChild(this.detailsElement_);
163
164       this.appendChild(contentElement);
165
166       var settings = new pluginSettings.Settings(this.info_.id);
167       this.updateRulesCount_(settings);
168       settings.addEventListener(
169           'change',
170           this.updateRulesCount_.bind(this, settings));
171
172       // Create the rule list asynchronously, to make sure that it is already
173       // fully integrated in the DOM tree.
174       window.setTimeout(this.loadRules_.bind(this, settings), 0);
175     },
176
177     /**
178      * Create the list of content setting rules applying to this plug-in.
179      * @param {!pluginSettings.Settings} The settings object storing the content
180      *     setting rules.
181      * @private
182      */
183     loadRules_: function(settings) {
184       var rulesEl = this.ownerDocument.createElement('list');
185       this.detailsElement_.appendChild(rulesEl);
186
187       pluginSettings.ui.RuleList.decorate(rulesEl);
188       rulesEl.setPluginSettings(settings);
189     },
190
191     /**
192      * Called when the list of rules changes to update the rule count shown when
193      * the list is not expanded.
194      * @param {!pluginSettings.Settings} The settings object storing the content
195      *     setting rules.
196      * @private
197      */
198     updateRulesCount_: function(settings) {
199       this.numRulesEl_.textContent = '(' + settings.getAll().length + ' rules)';
200     },
201
202     /**
203      * Whether this item is expanded or not.
204      * @type {boolean}
205      */
206     expanded_: false,
207     /**
208      * Whether this item is expanded or not.
209      * @type {boolean}
210      */
211     get expanded() {
212       return this.expanded_;
213     },
214     set expanded(expanded) {
215       if (this.expanded_ == expanded) {
216         return;
217       }
218       this.expanded_ = expanded;
219       if (expanded) {
220         var oldExpanded = this.list_.expandItem;
221         this.list_.expandItem = this;
222         this.detailsElement_.classList.remove(CSSClass.HIDDEN);
223         if (oldExpanded) {
224           oldExpanded.expanded = false;
225         }
226         this.classList.add(CSSClass.PLUGIN_SHOW_DETAILS);
227       } else {
228         if (this.list_.expandItem == this) {
229           this.list_.leadItemHeight = 0;
230           this.list_.expandItem = null;
231         }
232         this.style.height = '';
233         this.detailsElement_.classList.add(CSSClass.HIDDEN);
234         this.classList.remove(CSSClass.PLUGIN_SHOW_DETAILS);
235       }
236     },
237   };
238
239   /**
240    * Creates a new plug-in list.
241    * @constructor
242    * @extends {cr.ui.List}
243    */
244   var PluginList = cr.ui.define('list');
245
246   PluginList.prototype = {
247     __proto__: List.prototype,
248
249     /**
250      * Initializes the element.
251      */
252     decorate: function() {
253       List.prototype.decorate.call(this);
254       this.classList.add(CSSClass.PLUGIN_LIST);
255       var sm = new ListSingleSelectionModel();
256       sm.addEventListener('change', this.handleSelectionChange_.bind(this));
257       this.selectionModel = sm;
258       this.autoExpands = true;
259     },
260
261     /**
262      * Creates a new plug-in list item.
263      * @param {!Object} info Information about the plug-in.
264      */
265     createItem: function(info) {
266       return new PluginListItem(this, info);
267     },
268
269     /**
270      * Called when the selection changes.
271      * @param {!Event} ce The change event.
272      * @private
273      */
274     handleSelectionChange_: function(ce) {
275       ce.changes.forEach(function(change) {
276         var listItem = this.getListItemByIndex(change.index);
277         if (listItem) {
278           if (!change.selected) {
279             // TODO(bsmith) explain window timeout (from cookies_list.js)
280             window.setTimeout(function() {
281               if (!listItem.selected || !listItem.lead) {
282                 listItem.expanded = false;
283               }
284             }, 0);
285           } else if (listItem.lead) {
286             listItem.expanded = true;
287           }
288         }
289       }, this);
290     },
291   };
292
293   return {
294     PluginList: PluginList,
295     PluginListItem: PluginListItem,
296   };
297 });