Index: chrome/browser/resources/new_new_tab.js |
diff --git a/chrome/browser/resources/new_new_tab.js b/chrome/browser/resources/new_new_tab.js |
index 2255005c901ac48b6d06fb30f5788373b54d7c78..f78083701f7ad74a8d0647f3c88afc1bf97c5a9c 100644 |
--- a/chrome/browser/resources/new_new_tab.js |
+++ b/chrome/browser/resources/new_new_tab.js |
@@ -20,6 +20,7 @@ function recentlyClosedTabs(data) { |
// We need to store the recent items so we can update the layout on a resize. |
recentItems = data; |
renderRecentlyClosed(); |
+ layoutSections(); |
} |
var recentItems = []; |
@@ -87,6 +88,141 @@ function handleWindowResize() { |
mostVisited.layout(); |
renderRecentlyClosed(); |
} |
+ |
+ layoutSections(); |
+} |
+ |
+// Stores some information about each section necessary to layout. A new |
+// instance is constructed for each section on each layout. |
+function SectionLayoutInfo(section) { |
+ this.section = section; |
+ this.header = section.getElementsByTagName('h2')[0]; |
+ this.miniview = section.getElementsByClassName('miniview')[0]; |
+ this.maxiview = section.getElementsByClassName('maxiview')[0]; |
+ this.expanded = !section.classList.contains('hidden'); |
+ this.fixedHeight = this.header.offsetHeight; |
+ this.scrollingHeight = 0; |
+ |
+ if (this.expanded) { |
+ this.scrollingHeight = this.maxiview.offsetHeight; |
+ } else if (this.miniview) { |
+ this.fixedHeight += this.miniview.offsetHeight; |
+ } |
+} |
+ |
+// Get all sections to be layed out. |
+SectionLayoutInfo.getAll = function() { |
+ var sections = document.querySelectorAll('.section:not(.disabled)'); |
+ var result = []; |
+ for (var i = 0, section; section = sections[i]; i++) { |
+ result.push(new SectionLayoutInfo(section)); |
+ } |
+ return result; |
+}; |
+ |
+// Layout the sections in a modified accordian. The header and miniview, if |
arv (Not doing code reviews)
2010/08/31 21:13:59
accordion
|
+// visible are fixed within the viewport. If there is an expanded section, its |
+// it scrolls. |
+// |
+// ============================= |
+// | collapsed section | <- Any collapsed sections are fixed position. |
+// | and miniview | |
+// |---------------------------| |
+// | expanded section | |
+// | | <- There can be one expanded section and it |
+// | and maxiview | is absolutely positioned so that it can |
+// | | scroll "underneath" the fixed elements. |
+// | | |
+// |---------------------------| |
+// | another collapsed section | |
+// |---------------------------| |
+// |
+// We want the main frame scrollbar to be the one that scrolls the expanded |
+// region. To get this effect, we make the fixed elements position:fixed and the |
+// scrollable element position:absolute. We also artificially increase the |
+// height of the document so that it is possible to scroll down enough to |
+// display the end of the document, even with any fixed elements at the bottom |
+// of the viewport. |
+// |
+// There is a final twist: If the intrinsic height of the expanded section is |
+// less than the available height (because the window is tall), any collapsed |
+// sections sinch up and sit below the expanded section. This is so that we |
+// don't have a bunch of dead whitespace in the case of expanded sections that |
+// aren't very tall. |
+function layoutSections() { |
+ var sections = SectionLayoutInfo.getAll(); |
+ var expandedSection = null; |
+ var headerHeight = 0; |
+ var footerHeight = 0; |
+ |
+ // Calculate the height of the fixed elements above the expanded section. Also |
+ // take note of the expanded section, if there is one. |
+ var i; |
+ var section; |
+ for (i = 0; section = sections[i]; i++) { |
+ headerHeight += section.fixedHeight; |
+ if (section.expanded) { |
+ expandedSection = section; |
+ i++; |
+ break; |
+ } |
+ } |
+ |
+ // Calculate the height of the fixed elements below the expanded section, if |
+ // any. |
+ for (; section = sections[i]; i++) { |
+ footerHeight += section.fixedHeight; |
+ } |
+ |
+ // Determine the height to use for the expanded section. If there isn't enough |
+ // space to show the expanded section completely, this will be the available |
+ // height. Otherwise, we use the intrinsic height of the expanded section. |
+ var expandedSectionHeight; |
+ if (expandedSection) { |
+ var flexHeight = window.innerHeight - headerHeight - footerHeight; |
+ if (flexHeight < expandedSection.scrollingHeight) { |
+ expandedSectionHeight = flexHeight; |
+ |
+ // Also, artificially expand the height of the document so that we can see |
+ // the entire expanded section. |
+ // |
+ // TODO(aa): Where does this come from? It is the difference between what |
+ // we set document.body.style.height to and what |
+ // document.body.scrollHeight measures afterward. I expect them to be the |
+ // same if document.body has no margins. |
arv (Not doing code reviews)
2010/08/31 21:13:59
I think we had a padding or margin on the body to
|
+ var fudge = 44; |
+ document.body.style.height = |
+ headerHeight + |
+ expandedSection.scrollingHeight + |
+ footerHeight + |
+ fudge + |
+ 'px'; |
+ } else { |
+ expandedSectionHeight = expandedSection.scrollingHeight; |
+ document.body.style.height = ''; |
+ } |
+ } |
+ |
+ // Now position all the elements. |
+ var y = 0; |
+ for (i = 0, section; section = sections[i]; i++) { |
+ section.header.style.top = y + 'px'; |
+ y += section.header.offsetHeight; |
+ |
+ if (section.miniview) { |
+ section.miniview.style.top = y + 'px'; |
+ if (section != expandedSection) { |
+ y += section.miniview.offsetHeight; |
+ } |
+ } |
+ |
+ if (section.maxiview) { |
+ section.maxiview.style.top = y + 'px'; |
+ if (section == expandedSection) { |
+ y += expandedSectionHeight; |
+ } |
+ } |
+ } |
} |
window.addEventListener('resize', handleWindowResize); |
@@ -147,6 +283,7 @@ function setShownSections(newShownSections) { |
else |
hideSection(Section[key]); |
} |
+ layoutSections(); |
} |
// Recently closed |
@@ -303,20 +440,6 @@ function viewLog() { |
console.log(lines.join('\n')); |
} |
-// Updates the visibility of the menu items. |
-function updateOptionMenu() { |
- var menuItems = $('option-menu').children; |
- for (var i = 0; i < menuItems.length; i++) { |
- var item = menuItems[i]; |
- var command = item.getAttribute('command'); |
- if (command == 'show' || command == 'hide') { |
- var section = Section[item.getAttribute('section')]; |
- var visible = shownSections & section; |
- item.setAttribute('command', visible ? 'hide' : 'show'); |
- } |
- } |
-} |
- |
// We apply the size class here so that we don't trigger layout animations |
// onload. |
@@ -416,6 +539,7 @@ function showFirstRunNotification() { |
function OptionMenu(button, menu) { |
this.button = button; |
this.menu = menu; |
+ this.button.onclick = bind(this.handleClick, this); |
this.button.onmousedown = bind(this.handleMouseDown, this); |
this.button.onkeydown = bind(this.handleKeyDown, this); |
this.boundHideMenu_ = bind(this.hide, this); |
@@ -427,7 +551,6 @@ function OptionMenu(button, menu) { |
OptionMenu.prototype = { |
show: function() { |
- updateOptionMenu(); |
this.positionMenu_(); |
this.menu.style.display = 'block'; |
this.button.classList.add('open'); |
@@ -440,7 +563,9 @@ OptionMenu.prototype = { |
}, |
positionMenu_: function() { |
- this.menu.style.top = this.button.getBoundingClientRect().bottom + 'px'; |
+ var rect = this.button.getBoundingClientRect(); |
+ this.menu.style.top = rect.bottom + 'px'; |
+ this.menu.style.right = (document.body.clientWidth - rect.right) + 'px' |
}, |
hide: function() { |
@@ -476,6 +601,10 @@ OptionMenu.prototype = { |
} |
}, |
+ handleClick: function(e) { |
+ e.stopPropagation(); |
+ }, |
+ |
handleMouseOver: function(e) { |
var el = e.target; |
if (!el.hasAttribute('command')) { |
@@ -587,27 +716,15 @@ OptionMenu.prototype = { |
} |
}; |
-// TODO(aa): The 'clear-all-blacklisted' feature needs to move into a menu in |
-// the most visited section. |
-/* |
-var optionMenu = new OptionMenu($('option-button'), $('option-menu')); |
+var optionMenu = new OptionMenu( |
+ document.querySelector('#most-visited-section h2 .settings'), |
+ $('option-menu')); |
optionMenu.commands = { |
'clear-all-blacklisted' : function() { |
mostVisited.clearAllBlacklisted(); |
chrome.send('getMostVisited'); |
- }, |
- 'show': function(item) { |
- var section = Section[item.getAttribute('section')]; |
- showSection(section); |
- saveShownSections(); |
- }, |
- 'hide': function(item) { |
- var section = Section[item.getAttribute('section')]; |
- hideSection(section); |
- saveShownSections(); |
} |
}; |
-*/ |
$('main').addEventListener('click', function(e) { |
var p = e.target; |
@@ -626,10 +743,17 @@ $('main').addEventListener('click', function(e) { |
var section = p.getAttribute('section'); |
if (section) { |
- if (shownSections & Section[section]) |
+ if (shownSections & Section[section]) { |
hideSection(Section[section]); |
- else |
- showSection(Section[section]); |
+ } else { |
+ for (var p in Section) { |
+ if (p == section) |
+ showSection(Section[p]); |
+ else |
+ hideSection(Section[p]); |
+ } |
+ } |
+ layoutSections(); |
saveShownSections(); |
} |
}); |
@@ -821,8 +945,7 @@ function callGetSyncMessageIfSyncIsPresent() { |
} |
function hideAllMenus() { |
- // TODO(aa): See comment in definition of optionMenu. |
- //optionMenu.hide(); |
+ optionMenu.hide(); |
} |
window.addEventListener('blur', hideAllMenus); |
@@ -878,7 +1001,7 @@ updateAttribution(); |
var mostVisited = new MostVisited( |
$('most-visited'), |
- $('most-visited-section').getElementsByClassName('miniview')[0], |
+ document.querySelector('#most-visited-section .miniview'), |
useSmallGrid(), |
shownSections & Section.THUMB); |
@@ -887,6 +1010,7 @@ function mostVisitedPages(data, firstRun) { |
mostVisited.data = data; |
mostVisited.layout(); |
+ layoutSections(); |
loading = false; |