| // Copyright (c) 2012 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 */ var AutocompleteList = cr.ui.AutocompleteList; |
| /** @const */ var InlineEditableItem = options.InlineEditableItem; |
| /** @const */ var 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, |
| |
| /** @override */ |
| decorate: function() { |
| InlineEditableItem.prototype.decorate.call(this); |
| |
| var pageInfo = this.pageInfo_; |
| |
| if (pageInfo.modelIndex == -1) { |
| this.isPlaceholder = true; |
| pageInfo.title = loadTimeData.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 = getFaviconImageSet(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.className = 'weakrtl'; |
| urlField.placeholder = loadTimeData.getString('startupPagesPlaceholder'); |
| 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; |
| }, |
| |
| /** @override */ |
| get currentInputIsValid() { |
| return this.urlField_.validity.valid; |
| }, |
| |
| /** @override */ |
| 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, |
| |
| /** @override */ |
| 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)); |
| }, |
| |
| /** @override */ |
| createItem: function(pageInfo) { |
| var item = new StartupPageListItem(pageInfo); |
| item.urlField_.disabled = this.disabled; |
| return item; |
| }, |
| |
| /** @override */ |
| deleteItemAtIndex: function(index) { |
| chrome.send('removeStartupPages', [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); |
| |
| if (!(dropTarget instanceof StartupPageListItem) || |
| dropTarget.pageInfo_.modelIndex == -1) { |
| return; |
| } |
| |
| this.hideDropMarker_(); |
| |
| // Insert the selection at the new position. |
| var newIndex = this.dataModel.indexOf(dropTarget.pageInfo_); |
| if (this.dropPos == 'below') |
| newIndex += 1; |
| |
| // If there are selected indexes, it was a re-order. |
| if (this.selectionModel.selectedIndexes.length > 0) { |
| chrome.send('dragDropStartupPage', |
| [newIndex, this.selectionModel.selectedIndexes]); |
| return; |
| } |
| |
| // Otherwise it was potentially a drop of new data (e.g. a bookmark). |
| var url = e.dataTransfer.getData('url'); |
| if (url) { |
| e.preventDefault(); |
| chrome.send('addStartupPage', [url, newIndex]); |
| } |
| }, |
| |
| /** |
| * 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 |
| }; |
| }); |