- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / examples / extensions / plugin_settings / js / rule_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 content setting rules.
7  */
8
9 cr.define('pluginSettings.ui', function() {
10   const InlineEditableItemList = options.InlineEditableItemList;
11   const InlineEditableItem = options.InlineEditableItem;
12   const ArrayDataModel = cr.ui.ArrayDataModel;
13
14   /**
15    * CSS classes used by this class.
16    * @enum {string}
17    */
18   const CSSClass = {
19     /**
20      * A list of content setting rules.
21      */
22     RULE_LIST: 'rule-list',
23
24     /**
25      * The element containing the content setting pattern for a rule.
26      */
27     RULE_PATTERN: 'rule-pattern',
28
29     /**
30      * The element containing the behavior (allow or block) for a rule.
31      */
32     RULE_BEHAVIOR: 'rule-behavior',
33
34     /**
35      * Static text (as opposed to an editable text field).
36      */
37     STATIC_TEXT: 'static-text',
38   };
39   /**
40    * A single item in a list of rules.
41    * @param {!RuleList} list The rule list containing this item.
42    * @param {!Object} rule The content setting rule.
43    * @constructor
44    * @extends {options.InlineEditableItem}
45    */
46   function RuleListItem(list, rule) {
47     var el = cr.doc.createElement('li');
48
49     /**
50      * The content setting rule.
51      * @type {!Object}
52      * @private
53      */
54     el.dataItem_ = rule;
55
56     /**
57      * The rule list containing this item.
58      * @type {!RuleList}
59      * @private
60      */
61     el.list_ = list;
62     el.__proto__ = RuleListItem.prototype;
63     el.decorate();
64
65     return el;
66   }
67
68   RuleListItem.prototype = {
69     __proto__: InlineEditableItem.prototype,
70
71     /**
72      * The text input element for the pattern. This is only null in the
73      * prototype.
74      * @type {?HTMLInputElement}
75      * @private
76      */
77     input_: null,
78
79     /**
80      * The popup button for the setting. This is only null in the prototype.
81      * @type {?HTMLSelectElement}
82      * @private
83      */
84     select_: null,
85
86     /**
87      * The static text field containing the pattern.
88      * @type {?HTMLDivElement}
89      * @private
90      */
91     patternLabel_: null,
92
93     /**
94      * The static text field containing the setting.
95      * @type {?HTMLDivElement}
96      * @private
97      */
98     settingLabel_: null,
99
100     /**
101      * Decorates an elements as a list item.
102      */
103     decorate: function() {
104       InlineEditableItem.prototype.decorate.call(this);
105
106       this.isPlaceholder = !this.pattern;
107       var patternCell = this.createEditableTextCell(this.pattern);
108       patternCell.className = CSSClass.RULE_PATTERN;
109       this.contentElement.appendChild(patternCell);
110       var input = patternCell.querySelector('input');
111       if (this.pattern) {
112         this.patternLabel_ =
113             patternCell.querySelector('.' + CSSClass.STATIC_TEXT);
114       } else {
115         input.placeholder = chrome.i18n.getMessage('addNewPattern');
116       }
117
118       // TODO(stuartmorgan): Create an createEditableSelectCell abstracting
119       // this code.
120       // Setting label for display mode. |pattern| will be null for the 'add new
121       // exception' row.
122       if (this.pattern) {
123         var settingLabel = cr.doc.createElement('span');
124         settingLabel.textContent = this.settingForDisplay();
125         settingLabel.className = CSSClass.RULE_BEHAVIOR;
126         settingLabel.setAttribute('displaymode', 'static');
127         this.contentElement.appendChild(settingLabel);
128         this.settingLabel_ = settingLabel;
129       }
130
131       // Setting select element for edit mode.
132       var select = cr.doc.createElement('select');
133       var optionAllow = cr.doc.createElement('option');
134       optionAllow.textContent = chrome.i18n.getMessage('allowRule');
135       optionAllow.value = 'allow';
136       select.appendChild(optionAllow);
137
138       var optionBlock = cr.doc.createElement('option');
139       optionBlock.textContent = chrome.i18n.getMessage('blockRule');
140       optionBlock.value = 'block';
141       select.appendChild(optionBlock);
142
143       this.contentElement.appendChild(select);
144       select.className = CSSClass.RULE_BEHAVIOR;
145       if (this.pattern) {
146         select.setAttribute('displaymode', 'edit');
147       }
148
149       this.input_ = input;
150       this.select_ = select;
151
152       this.updateEditables();
153
154       // Listen for edit events.
155       this.addEventListener('canceledit', this.onEditCancelled_);
156       this.addEventListener('commitedit', this.onEditCommitted_);
157     },
158
159     /**
160      * The pattern (e.g., a URL) for the rule.
161      * @type {string}
162      */
163     get pattern() {
164       return this.dataItem_['primaryPattern'];
165     },
166     set pattern(pattern) {
167       this.dataItem_['primaryPattern'] = pattern;
168     },
169
170     /**
171      * The setting (allow/block) for the rule.
172      * @type {string}
173      */
174     get setting() {
175       return this.dataItem_['setting'];
176     },
177     set setting(setting) {
178       this.dataItem_['setting'] = setting;
179     },
180
181     /**
182      * Gets a human-readable setting string.
183      * @type {string}
184      */
185     settingForDisplay: function() {
186       var setting = this.setting;
187       if (setting == 'allow') {
188         return chrome.i18n.getMessage('allowRule');
189       }
190       if (setting == 'block') {
191         return chrome.i18n.getMessage('blockRule');
192       }
193     },
194
195     /**
196      * Set the <input> to its original contents. Used when the user quits
197      * editing.
198      */
199     resetInput: function() {
200       this.input_.value = this.pattern;
201     },
202
203     /**
204      * Copy the data model values to the editable nodes.
205      */
206     updateEditables: function() {
207       this.resetInput();
208
209       var settingOption =
210           this.select_.querySelector('[value=\'' + this.setting + '\']');
211       if (settingOption) {
212         settingOption.selected = true;
213       }
214     },
215
216     /** @inheritDoc */
217     get hasBeenEdited() {
218       var livePattern = this.input_.value;
219       var liveSetting = this.select_.value;
220       return livePattern != this.pattern || liveSetting != this.setting;
221     },
222
223     /**
224      * Called when committing an edit.
225      * @param {!Event} e The end event.
226      * @private
227      */
228     onEditCommitted_: function(e) {
229       var newPattern = this.input_.value;
230       var newSetting = this.select_.value;
231
232       this.finishEdit(newPattern, newSetting);
233     },
234
235     /**
236      * Called when cancelling an edit; resets the control states.
237      * @param {!Event} e The cancel event.
238      * @private
239      */
240     onEditCancelled_: function() {
241       this.updateEditables();
242     },
243
244     /**
245      * Editing is complete; update the model.
246      * @param {string} newPattern The pattern that the user entered.
247      * @param {string} newSetting The setting the user chose.
248      */
249     finishEdit: function(newPattern, newSetting) {
250       this.patternLabel_.textContent = newPattern;
251       this.settingLabel_.textContent = this.settingForDisplay();
252       var oldPattern = this.pattern;
253       this.pattern = newPattern;
254       this.setting = newSetting;
255
256       this.list_.settings.update(oldPattern, newPattern, newSetting,
257                                  this.list_.settingsChangedCallback());
258     }
259   };
260
261   /**
262    * Create a new list item to add a rule.
263    * @param {!RuleList} list The rule list containing this item.
264    * @constructor
265    * @extends {AddRuleListItem}
266    */
267   function AddRuleListItem(list) {
268     var el = cr.doc.createElement('div');
269     el.dataItem_ = {};
270     el.list_ = list;
271     el.__proto__ = AddRuleListItem.prototype;
272     el.decorate();
273
274     return el;
275   }
276
277   AddRuleListItem.prototype = {
278     __proto__: RuleListItem.prototype,
279
280     /**
281      * Initializes the element.
282      */
283     decorate: function() {
284       RuleListItem.prototype.decorate.call(this);
285
286       this.setting = 'allow';
287     },
288
289     /**
290      * Clear the <input> and let the placeholder text show again.
291      */
292     resetInput: function() {
293       this.input_.value = '';
294     },
295
296     /** @inheritDoc */
297     get hasBeenEdited() {
298       return this.input_.value != '';
299     },
300
301     /**
302      * Editing is complete; update the model. As long as the pattern isn't
303      * empty, we'll just add it.
304      * @param {string} newPattern The pattern that the user entered.
305      * @param {string} newSetting The setting the user chose.
306      */
307     finishEdit: function(newPattern, newSetting) {
308       this.resetInput();
309       this.list_.settings.set(newPattern, newSetting,
310                               this.list_.settingsChangedCallback());
311     },
312   };
313
314   /**
315    * A list of content setting rules.
316    * @constructor
317    * @extends {cr.ui.List}
318    */
319   var RuleList = cr.ui.define('list');
320
321   RuleList.prototype = {
322     __proto__: InlineEditableItemList.prototype,
323
324     /**
325      * The content settings model for this list.
326      * @type {?Settings}
327      */
328     settings: null,
329
330     /**
331      * Called when an element is decorated as a list.
332      */
333     decorate: function() {
334       InlineEditableItemList.prototype.decorate.call(this);
335
336       this.classList.add(CSSClass.RULE_LIST);
337
338       this.autoExpands = true;
339       this.reset();
340     },
341
342     /**
343      * Creates an item to go in the list.
344      * @param {?Object} entry The element from the data model for this row.
345      */
346     createItem: function(entry) {
347       if (entry) {
348         return new RuleListItem(this, entry);
349       } else {
350         var addRuleItem = new AddRuleListItem(this);
351         addRuleItem.deletable = false;
352         return addRuleItem;
353       }
354     },
355
356     /**
357      * Sets the rules in the js model.
358      * @param {!Array} entries A list of dictionaries of values, each dictionary
359      *     represents a rule.
360      */
361     setRules_: function(entries) {
362       var deleteCount = this.dataModel.length - 1;
363
364       var args = [0, deleteCount];
365       args.push.apply(args, entries);
366       this.dataModel.splice.apply(this.dataModel, args);
367     },
368
369     /**
370      * Called when the list of content setting rules has been changed.
371      * @param {?string} error The error message, if an error occurred.
372      *     Otherwise, this is null.
373      * @private
374      */
375     settingsChanged_: function(error) {
376       if (error) {
377         $('error').textContent = 'Error: ' + error;
378       } else {
379         $('error').textContent = '';
380       }
381       this.setRules_(this.settings.getAll());
382     },
383
384     /**
385      * @return {function()} A bound callback to update the UI after the
386      *     settings have been changed.
387      */
388     settingsChangedCallback: function() {
389       return this.settingsChanged_.bind(this);
390     },
391
392     /**
393      * Binds this list to the content settings model.
394      * @param {!Settings} settings The content settings model.
395      */
396     setPluginSettings: function(settings) {
397       this.settings = settings;
398       this.settingsChanged_();
399     },
400
401     /**
402      * Removes all rules from the js model.
403      */
404     reset: function() {
405       // The null creates the Add New Rule row.
406       this.dataModel = new ArrayDataModel([null]);
407     },
408
409     /** @inheritDoc */
410     deleteItemAtIndex: function(index) {
411       var listItem = this.getListItemByIndex(index);
412       if (listItem.undeletable) {
413         return;
414       }
415
416       this.settings.clear(listItem.pattern, this.settingsChangedCallback());
417     },
418   };
419
420   return {
421     RuleListItem: RuleListItem,
422     AddRuleListItem: AddRuleListItem,
423     RuleList: RuleList,
424   }
425 });