Index: chrome/browser/resources/options2/browser_options_startup_page_list.js |
diff --git a/chrome/browser/resources/options2/browser_options_startup_page_list.js b/chrome/browser/resources/options2/browser_options_startup_page_list.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cd107293c40a2228167f55ca35be481aae1359c1 |
--- /dev/null |
+++ b/chrome/browser/resources/options2/browser_options_startup_page_list.js |
@@ -0,0 +1,310 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+cr.define('options.browser_options', function() { |
+ const AutocompleteList = options.AutocompleteList; |
+ const InlineEditableItem = options.InlineEditableItem; |
+ const InlineEditableItemList = options.InlineEditableItemList; |
+ |
+ /** |
+ * Creates a new startup page list item. |
+ * @param {Object} pageInfo The page this item represents. |
+ * @constructor |
+ * @extends {cr.ui.ListItem} |
+ */ |
+ function StartupPageListItem(pageInfo) { |
+ var el = cr.doc.createElement('div'); |
+ el.pageInfo_ = pageInfo; |
+ StartupPageListItem.decorate(el); |
+ return el; |
+ } |
+ |
+ /** |
+ * Decorates an element as a startup page list item. |
+ * @param {!HTMLElement} el The element to decorate. |
+ */ |
+ StartupPageListItem.decorate = function(el) { |
+ el.__proto__ = StartupPageListItem.prototype; |
+ el.decorate(); |
+ }; |
+ |
+ StartupPageListItem.prototype = { |
+ __proto__: InlineEditableItem.prototype, |
+ |
+ /** |
+ * Input field for editing the page url. |
+ * @type {HTMLElement} |
+ * @private |
+ */ |
+ urlField_: null, |
+ |
+ /** @inheritDoc */ |
+ decorate: function() { |
+ InlineEditableItem.prototype.decorate.call(this); |
+ |
+ var pageInfo = this.pageInfo_; |
+ |
+ if (pageInfo['modelIndex'] == '-1') { |
+ this.isPlaceholder = true; |
+ pageInfo['title'] = localStrings.getString('startupAddLabel'); |
+ pageInfo['url'] = ''; |
+ } |
+ |
+ var titleEl = this.ownerDocument.createElement('div'); |
+ titleEl.className = 'title'; |
+ titleEl.classList.add('favicon-cell'); |
+ titleEl.classList.add('weakrtl'); |
+ titleEl.textContent = pageInfo['title']; |
+ if (!this.isPlaceholder) { |
+ titleEl.style.backgroundImage = url('chrome://favicon/' + |
+ pageInfo['url']); |
+ titleEl.title = pageInfo['tooltip']; |
+ } |
+ |
+ this.contentElement.appendChild(titleEl); |
+ |
+ var urlEl = this.createEditableTextCell(pageInfo['url']); |
+ urlEl.className = 'url'; |
+ urlEl.classList.add('weakrtl'); |
+ this.contentElement.appendChild(urlEl); |
+ |
+ var urlField = urlEl.querySelector('input') |
+ urlField.required = true; |
+ urlField.className = 'weakrtl'; |
+ this.urlField_ = urlField; |
+ |
+ this.addEventListener('commitedit', this.onEditCommitted_); |
+ |
+ var self = this; |
+ urlField.addEventListener('focus', function(event) { |
+ self.parentNode.autocompleteList.attachToInput(urlField); |
+ }); |
+ urlField.addEventListener('blur', function(event) { |
+ self.parentNode.autocompleteList.detach(); |
+ }); |
+ |
+ if (!this.isPlaceholder) |
+ this.draggable = true; |
+ }, |
+ |
+ /** @inheritDoc */ |
+ get currentInputIsValid() { |
+ return this.urlField_.validity.valid; |
+ }, |
+ |
+ /** @inheritDoc */ |
+ get hasBeenEdited() { |
+ return this.urlField_.value != this.pageInfo_['url']; |
+ }, |
+ |
+ /** |
+ * Called when committing an edit; updates the model. |
+ * @param {Event} e The end event. |
+ * @private |
+ */ |
+ onEditCommitted_: function(e) { |
+ var url = this.urlField_.value; |
+ if (this.isPlaceholder) |
+ chrome.send('addStartupPage', [url]); |
+ else |
+ chrome.send('editStartupPage', [this.pageInfo_['modelIndex'], url]); |
+ }, |
+ }; |
+ |
+ var StartupPageList = cr.ui.define('list'); |
+ |
+ StartupPageList.prototype = { |
+ __proto__: InlineEditableItemList.prototype, |
+ |
+ /** |
+ * An autocomplete suggestion list for URL editing. |
+ * @type {AutocompleteList} |
+ */ |
+ autocompleteList: null, |
+ |
+ /** |
+ * The drop position information: "below" or "above". |
+ */ |
+ dropPos: null, |
+ |
+ /** @inheritDoc */ |
+ decorate: function() { |
+ InlineEditableItemList.prototype.decorate.call(this); |
+ |
+ // Listen to drag and drop events. |
+ this.addEventListener('dragstart', this.handleDragStart_.bind(this)); |
+ this.addEventListener('dragenter', this.handleDragEnter_.bind(this)); |
+ this.addEventListener('dragover', this.handleDragOver_.bind(this)); |
+ this.addEventListener('drop', this.handleDrop_.bind(this)); |
+ this.addEventListener('dragleave', this.handleDragLeave_.bind(this)); |
+ this.addEventListener('dragend', this.handleDragEnd_.bind(this)); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ createItem: function(pageInfo) { |
+ var item = new StartupPageListItem(pageInfo); |
+ item.urlField_.disabled = this.disabled; |
+ return item; |
+ }, |
+ |
+ /** @inheritDoc */ |
+ deleteItemAtIndex: function(index) { |
+ chrome.send('removeStartupPages', [String(index)]); |
+ }, |
+ |
+ /* |
+ * Computes the target item of drop event. |
+ * @param {Event} e The drop or dragover event. |
+ * @private |
+ */ |
+ getTargetFromDropEvent_ : function(e) { |
+ var target = e.target; |
+ // e.target may be an inner element of the list item |
+ while (target != null && !(target instanceof StartupPageListItem)) { |
+ target = target.parentNode; |
+ } |
+ return target; |
+ }, |
+ |
+ /* |
+ * Handles the dragstart event. |
+ * @param {Event} e The dragstart event. |
+ * @private |
+ */ |
+ handleDragStart_: function(e) { |
+ // Prevent dragging if the list is disabled. |
+ if (this.disabled) { |
+ e.preventDefault(); |
+ return false; |
+ } |
+ |
+ var target = e.target; |
+ // StartupPageListItem should be the only draggable element type in the |
+ // page but let's make sure. |
+ if (target instanceof StartupPageListItem) { |
+ this.draggedItem = target; |
+ this.draggedItem.editable = false; |
+ e.dataTransfer.effectAllowed = 'move'; |
+ // We need to put some kind of data in the drag or it will be |
+ // ignored. Use the URL in case the user drags to a text field or the |
+ // desktop. |
+ e.dataTransfer.setData('text/plain', target.urlField_.value); |
+ } |
+ }, |
+ |
+ /* |
+ * Handles the dragenter event. |
+ * @param {Event} e The dragenter event. |
+ * @private |
+ */ |
+ handleDragEnter_: function(e) { |
+ e.preventDefault(); |
+ }, |
+ |
+ /* |
+ * Handles the dragover event. |
+ * @param {Event} e The dragover event. |
+ * @private |
+ */ |
+ handleDragOver_: function(e) { |
+ var dropTarget = this.getTargetFromDropEvent_(e); |
+ // Determines whether the drop target is to accept the drop. |
+ // The drop is only successful on another StartupPageListItem. |
+ if (!(dropTarget instanceof StartupPageListItem) || |
+ dropTarget == this.draggedItem || dropTarget.isPlaceholder) { |
+ this.hideDropMarker_(); |
+ return; |
+ } |
+ // Compute the drop postion. Should we move the dragged item to |
+ // below or above the drop target? |
+ var rect = dropTarget.getBoundingClientRect(); |
+ var dy = e.clientY - rect.top; |
+ var yRatio = dy / rect.height; |
+ var dropPos = yRatio <= .5 ? 'above' : 'below'; |
+ this.dropPos = dropPos; |
+ this.showDropMarker_(dropTarget, dropPos); |
+ e.preventDefault(); |
+ }, |
+ |
+ /* |
+ * Handles the drop event. |
+ * @param {Event} e The drop event. |
+ * @private |
+ */ |
+ handleDrop_: function(e) { |
+ var dropTarget = this.getTargetFromDropEvent_(e); |
+ this.hideDropMarker_(); |
+ |
+ // Insert the selection at the new position. |
+ var newIndex = this.dataModel.indexOf(dropTarget.pageInfo_); |
+ if (this.dropPos == 'below') |
+ newIndex += 1; |
+ |
+ var selected = this.selectionModel.selectedIndexes; |
+ var stringized_selected = []; |
+ for (var j = 0; j < selected.length; j++) |
+ stringized_selected.push(String(selected[j])); |
+ |
+ chrome.send('dragDropStartupPage', |
+ [String(newIndex), stringized_selected] ); |
+ }, |
+ |
+ /* |
+ * Handles the dragleave event. |
+ * @param {Event} e The dragleave event |
+ * @private |
+ */ |
+ handleDragLeave_: function(e) { |
+ this.hideDropMarker_(); |
+ }, |
+ |
+ /** |
+ * Handles the dragend event. |
+ * @param {Event} e The dragend event |
+ * @private |
+ */ |
+ handleDragEnd_: function(e) { |
+ this.draggedItem.editable = true; |
+ this.draggedItem.updateEditState(); |
+ }, |
+ |
+ /* |
+ * Shows and positions the marker to indicate the drop target. |
+ * @param {HTMLElement} target The current target list item of drop |
+ * @param {string} pos 'below' or 'above' |
+ * @private |
+ */ |
+ showDropMarker_ : function(target, pos) { |
+ window.clearTimeout(this.hideDropMarkerTimer_); |
+ var marker = $('startupPagesListDropmarker'); |
+ var rect = target.getBoundingClientRect(); |
+ var markerHeight = 6; |
+ if (pos == 'above') { |
+ marker.style.top = (rect.top - markerHeight/2) + 'px'; |
+ } else { |
+ marker.style.top = (rect.bottom - markerHeight/2) + 'px'; |
+ } |
+ marker.style.width = rect.width + 'px'; |
+ marker.style.left = rect.left + 'px'; |
+ marker.style.display = 'block'; |
+ }, |
+ |
+ /* |
+ * Hides the drop marker. |
+ * @private |
+ */ |
+ hideDropMarker_ : function() { |
+ // Hide the marker in a timeout to reduce flickering as we move between |
+ // valid drop targets. |
+ window.clearTimeout(this.hideDropMarkerTimer_); |
+ this.hideDropMarkerTimer_ = window.setTimeout(function() { |
+ $('startupPagesListDropmarker').style.display = ''; |
+ }, 100); |
+ }, |
+ }; |
+ |
+ return { |
+ StartupPageList: StartupPageList |
+ }; |
+}); |