Bug 729878 - Part 1 - New layout implementation; r=dietrich,dao ui-r=shorlander

This commit is contained in:
Tim Taubert
2012-03-13 03:23:01 +01:00
parent 89daed0049
commit 6681c35b45
17 changed files with 649 additions and 815 deletions

View File

@@ -15,16 +15,14 @@ function Cell(aGrid, aNode) {
this._node._newtabCell = this;
// Register drag-and-drop event handlers.
["DragEnter", "DragOver", "DragExit", "Drop"].forEach(function (aType) {
let method = "on" + aType;
this[method] = this[method].bind(this);
this._node.addEventListener(aType.toLowerCase(), this[method], false);
["dragenter", "dragover", "dragexit", "drop"].forEach(function (aType) {
this._node.addEventListener(aType, this, false);
}, this);
}
Cell.prototype = {
/**
*
* The grid.
*/
_grid: null,
@@ -97,41 +95,27 @@ Cell.prototype = {
},
/**
* Event handler for the 'dragenter' event.
* @param aEvent The dragenter event.
* Handles all cell events.
*/
onDragEnter: function Cell_onDragEnter(aEvent) {
if (gDrag.isValid(aEvent)) {
aEvent.preventDefault();
gDrop.enter(this, aEvent);
}
},
handleEvent: function Cell_handleEvent(aEvent) {
if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent))
return;
/**
* Event handler for the 'dragover' event.
* @param aEvent The dragover event.
*/
onDragOver: function Cell_onDragOver(aEvent) {
if (gDrag.isValid(aEvent))
aEvent.preventDefault();
},
/**
* Event handler for the 'dragexit' event.
* @param aEvent The dragexit event.
*/
onDragExit: function Cell_onDragExit(aEvent) {
gDrop.exit(this, aEvent);
},
/**
* Event handler for the 'drop' event.
* @param aEvent The drop event.
*/
onDrop: function Cell_onDrop(aEvent) {
if (gDrag.isValid(aEvent)) {
aEvent.preventDefault();
gDrop.drop(this, aEvent);
switch (aEvent.type) {
case "dragenter":
aEvent.preventDefault();
gDrop.enter(this, aEvent);
break;
case "dragover":
aEvent.preventDefault();
break;
case "dragexit":
gDrop.exit(this, aEvent);
break;
case "drop":
aEvent.preventDefault();
gDrop.drop(this, aEvent);
break;
}
}
};

View File

@@ -36,11 +36,11 @@ let gDrag = {
start: function Drag_start(aSite, aEvent) {
this._draggedSite = aSite;
// Prevent moz-transform for left, top.
aSite.node.setAttribute("dragged", "true");
// Make sure the dragged site is floating above the grid.
aSite.node.setAttribute("ontop", "true");
// Mark nodes as being dragged.
let selector = ".newtab-site, .newtab-control, .newtab-thumbnail";
let nodes = aSite.node.parentNode.querySelectorAll(selector);
for (let i = 0; i < nodes.length; i++)
nodes[i].setAttribute("dragged", "true");
this._setDragData(aSite, aEvent);
@@ -88,13 +88,12 @@ let gDrag = {
* @param aEvent The 'dragend' event.
*/
end: function Drag_end(aSite, aEvent) {
aSite.node.removeAttribute("dragged");
let nodes = aSite.node.parentNode.querySelectorAll("[dragged]");
for (let i = 0; i < nodes.length; i++)
nodes[i].removeAttribute("dragged");
// Slide the dragged site back into its cell (may be the old or the new cell).
gTransformation.slideSiteTo(aSite, aSite.cell, {
unfreeze: true,
callback: function () aSite.node.removeAttribute("ontop")
});
gTransformation.slideSiteTo(aSite, aSite.cell, {unfreeze: true});
this._draggedSite = null;
},
@@ -132,13 +131,13 @@ let gDrag = {
// Create and use an empty drag element. We don't want to use the default
// drag image with its default opacity.
let dragElement = document.createElementNS(HTML_NAMESPACE, "div");
dragElement.classList.add("drag-element");
let body = document.getElementById("body");
body.appendChild(dragElement);
dragElement.classList.add("newtab-drag");
let scrollbox = document.getElementById("newtab-scrollbox");
scrollbox.appendChild(dragElement);
dt.setDragImage(dragElement, 0, 0);
// After the 'dragstart' event has been processed we can remove the
// temporary drag element from the DOM.
setTimeout(function () body.removeChild(dragElement), 0);
setTimeout(function () scrollbox.removeChild(dragElement), 0);
}
};

View File

@@ -26,13 +26,26 @@ let gDropTargetShim = {
init: function DropTargetShim_init() {
let node = gGrid.node;
this._dragover = this._dragover.bind(this);
// Add drag event handlers.
node.addEventListener("dragstart", this._start.bind(this), true);
// XXX bug 505521 - Don't listen for drag, it's useless at the moment.
//node.addEventListener("drag", this._drag.bind(this), false);
node.addEventListener("dragend", this._end.bind(this), true);
node.addEventListener("dragstart", this, true);
node.addEventListener("dragend", this, true);
},
/**
* Handles all shim events.
*/
handleEvent: function DropTargetShim_handleEvent(aEvent) {
switch (aEvent.type) {
case "dragstart":
this._start(aEvent);
break;
case "dragover":
this._dragover(aEvent);
break;
case "dragend":
this._end(aEvent);
break;
}
},
/**
@@ -40,11 +53,11 @@ let gDropTargetShim = {
* @param aEvent The 'dragstart' event.
*/
_start: function DropTargetShim_start(aEvent) {
if (aEvent.target.classList.contains("site")) {
if (aEvent.target.classList.contains("newtab-link")) {
gGrid.lock();
// XXX bug 505521 - Listen for dragover on the document.
document.documentElement.addEventListener("dragover", this._dragover, false);
document.documentElement.addEventListener("dragover", this, false);
}
},
@@ -56,12 +69,7 @@ let gDropTargetShim = {
// Let's see if we find a drop target.
let target = this._findDropTarget(aEvent);
if (target == this._lastDropTarget) {
// XXX bug 505521 - Don't fire dragover for now (causes recursion).
/*if (target)
// The last drop target is valid and didn't change.
this._dispatchEvent(aEvent, "dragover", target);*/
} else {
if (target != this._lastDropTarget) {
if (this._lastDropTarget)
// We left the last drop target.
this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
@@ -84,7 +92,7 @@ let gDropTargetShim = {
* @param aEvent The 'dragover' event.
*/
_dragover: function DropTargetShim_dragover(aEvent) {
let sourceNode = aEvent.dataTransfer.mozSourceNode;
let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode;
gDrag.drag(sourceNode._newtabSite, aEvent);
this._drag(aEvent);
@@ -117,7 +125,7 @@ let gDropTargetShim = {
gGrid.unlock();
// XXX bug 505521 - Remove the document's dragover listener.
document.documentElement.removeEventListener("dragover", this._dragover, false);
document.documentElement.removeEventListener("dragover", this, false);
},
/**

View File

@@ -24,7 +24,7 @@ let gGrid = {
*/
get cells() {
let cells = [];
let children = this.node.querySelectorAll("li");
let children = this.node.querySelectorAll(".newtab-cell");
for (let i = 0; i < children.length; i++)
cells.push(new Cell(this, children[i]));
@@ -43,8 +43,8 @@ let gGrid = {
* Initializes the grid.
* @param aSelector The query selector of the grid.
*/
init: function Grid_init(aSelector) {
this._node = document.querySelector(aSelector);
init: function Grid_init() {
this._node = document.getElementById("newtab-grid");
this._createSiteFragment();
this._draw();
},
@@ -96,21 +96,20 @@ let gGrid = {
* Creates the DOM fragment that is re-used when creating sites.
*/
_createSiteFragment: function Grid_createSiteFragment() {
let site = document.createElementNS(HTML_NAMESPACE, "a");
site.classList.add("site");
let site = document.createElementNS(HTML_NAMESPACE, "div");
site.classList.add("newtab-site");
site.setAttribute("draggable", "true");
// Create the site's inner HTML code.
site.innerHTML =
'<img class="site-img" width="' + THUMB_WIDTH +'" ' +
' height="' + THUMB_HEIGHT + '" alt=""/>' +
'<span class="site-title"/>' +
'<span class="site-strip">' +
' <input class="button strip-button strip-button-pin" type="button"' +
' tabindex="-1" title="' + newTabString("pin") + '"/>' +
' <input class="button strip-button strip-button-block" type="button"' +
' tabindex="-1" title="' + newTabString("block") + '"/>' +
'</span>';
'<a class="newtab-link">' +
' <span class="newtab-thumbnail"/>' +
' <span class="newtab-title"/>' +
'</a>' +
'<input type="button" title="' + newTabString("pin") + '"' +
' class="newtab-control newtab-control-pin"/>' +
'<input type="button" title="' + newTabString("block") + '"' +
' class="newtab-control newtab-control-block"/>';
this._siteFragment = document.createDocumentFragment();
this._siteFragment.appendChild(site);

View File

@@ -1,161 +1,174 @@
:root {
-moz-appearance: none;
-moz-user-focus: normal;
}
#scrollbox:not([page-disabled]) {
input[type=button] {
cursor: pointer;
}
/* SCROLLBOX */
#newtab-scrollbox {
display: -moz-box;
position: relative;
-moz-box-flex: 1;
}
#newtab-scrollbox:not([page-disabled]) {
overflow: auto;
}
#body {
position: relative;
margin: 0;
min-width: 675px;
-moz-user-select: none;
}
.button {
cursor: pointer;
}
/* TOOLBAR */
#toolbar {
/* TOGGLE */
#newtab-toggle {
position: absolute;
top: 12px;
right: 12px;
}
#toolbar[page-disabled] {
position: fixed;
}
#toolbar:-moz-locale-dir(rtl) {
left: 8px;
#newtab-toggle:-moz-locale-dir(rtl) {
left: 12px;
right: auto;
}
.toolbar-button {
position: absolute;
cursor: pointer;
-moz-transition: opacity 200ms ease-out;
/* MARGINS */
#newtab-vertical-margin {
display: -moz-box;
position: relative;
-moz-box-flex: 1;
-moz-box-orient: vertical;
}
#toolbar-button-show,
#toolbar-button-reset {
opacity: 0;
pointer-events: none;
#newtab-margin-top {
min-height: 50px;
max-height: 80px;
-moz-box-flex: 1;
}
#toolbar-button-reset[modified],
#toolbar-button-show[page-disabled] {
opacity: 1;
pointer-events: auto;
#newtab-margin-bottom {
min-height: 40px;
max-height: 100px;
-moz-box-flex: 1;
}
#toolbar-button-hide[page-disabled],
#toolbar-button-reset[page-disabled] {
opacity: 0;
pointer-events: none;
#newtab-horizontal-margin {
display: -moz-box;
-moz-box-flex: 5;
}
.newtab-side-margin {
min-width: 40px;
max-width: 300px;
-moz-box-flex: 1;
}
/* GRID */
#grid {
width: 637px;
height: 411px;
overflow: hidden;
list-style-type: none;
-moz-transition: opacity 200ms ease-out;
#newtab-grid {
display: -moz-box;
-moz-box-flex: 5;
-moz-box-orient: vertical;
min-width: 600px;
min-height: 400px;
-moz-transition: 100ms ease-out;
-moz-transition-property: opacity;
}
#grid[page-disabled] {
#newtab-grid[page-disabled] {
opacity: 0;
}
#grid[page-disabled],
#grid[locked] {
#newtab-grid[locked],
#newtab-grid[page-disabled] {
pointer-events: none;
}
/* ROWS */
.newtab-row {
display: -moz-box;
-moz-box-orient: horizontal;
-moz-box-direction: normal;
-moz-box-flex: 1;
}
/* CELLS */
.cell {
float: left;
width: 201px;
height: 127px;
margin-bottom: 15px;
-moz-margin-end: 16px;
}
.cell:-moz-locale-dir(rtl) {
float: right;
}
.cell:nth-child(3n+3) {
-moz-margin-end: 0;
.newtab-cell {
display: -moz-box;
-moz-box-flex: 1;
}
/* SITES */
.site {
display: block;
.newtab-site {
position: relative;
width: 201px;
height: 127px;
-moz-box-flex: 1;
-moz-transition: 100ms ease-out;
-moz-transition-property: top, left, opacity;
}
.site[frozen] {
.newtab-site[frozen] {
position: absolute;
pointer-events: none;
}
.site[ontop] {
.newtab-site[dragged] {
-moz-transition-property: none;
z-index: 10;
}
/* SITE IMAGE */
.site-img {
display: block;
opacity: 0.75;
-moz-transition: opacity 200ms ease-out;
}
.site:hover > .site-img,
.site[ontop] > .site-img,
.site:-moz-focusring > .site-img {
opacity: 1;
}
.site-img[loading] {
display: none;
}
/* SITE TITLE */
.site-title {
position: absolute;
left: 0;
bottom: 0;
overflow: hidden;
}
/* SITE STRIP */
.site-strip {
/* LINK + THUMBNAILS */
.newtab-link,
.newtab-thumbnail {
position: absolute;
left: 0;
top: 0;
width: 195px;
height: 17px;
overflow: hidden;
opacity: 0;
-moz-transition: opacity 200ms ease-out;
right: 0;
bottom: 0;
}
.site:hover:not([frozen]) > .site-strip {
.newtab-thumbnail {
opacity: .8;
-moz-transition: opacity 100ms ease-out;
}
.newtab-thumbnail[dragged],
.newtab-link:-moz-focusring > .newtab-thumbnail,
.newtab-site:hover > .newtab-link > .newtab-thumbnail {
opacity: 1;
}
.strip-button-pin,
.strip-button-block:-moz-locale-dir(rtl) {
float: left;
/* TITLES */
.newtab-title {
position: absolute;
left: 0;
right: 0;
bottom: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.strip-button-block,
.strip-button-pin:-moz-locale-dir(rtl) {
float: right;
/* CONTROLS */
.newtab-control {
position: absolute;
top: 4px;
opacity: 0;
-moz-transition: opacity 100ms ease-out;
}
.newtab-control:-moz-focusring,
.newtab-site:hover > .newtab-control {
opacity: 1;
}
.newtab-control[dragged] {
opacity: 0 !important;
}
.newtab-control-pin:-moz-locale-dir(ltr),
.newtab-control-block:-moz-locale-dir(rtl) {
left: 4px;
}
.newtab-control-block:-moz-locale-dir(ltr),
.newtab-control-pin:-moz-locale-dir(rtl) {
right: 4px;
}
/* DRAG & DROP */
@@ -165,7 +178,7 @@
* so that we can use custom drag images and elements. It needs an opacity of
* 0.01 so that the core code detects that it's in fact a visible element.
*/
.drag-element {
.newtab-drag {
width: 1px;
height: 1px;
background-color: #fff;

View File

@@ -30,13 +30,10 @@ XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name);
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
const THUMB_WIDTH = 201;
const THUMB_HEIGHT = 127;
#include batch.js
#include transformations.js
#include page.js
#include toolbar.js
#include grid.js
#include cells.js
#include sites.js
@@ -47,4 +44,4 @@ const THUMB_HEIGHT = 127;
#include updater.js
// Everything is loaded. Initialize the New Tab Page.
gPage.init("#toolbar", "#grid");
gPage.init();

View File

@@ -4,7 +4,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
@@ -13,28 +13,44 @@
%newTabDTD;
]>
<xul:window xmlns="http://www.w3.org/1999/xhtml"
<xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
disablefastfind="true" title="&newtab.pageTitle;">
<xul:vbox id="scrollbox" flex="1" title=" ">
<body id="body">
<div id="toolbar">
<input class="button toolbar-button" id="toolbar-button-show"
type="button" title="&newtab.show;"/>
<input class="button toolbar-button" id="toolbar-button-hide"
type="button" title="&newtab.hide;"/>
<input class="button toolbar-button" id="toolbar-button-reset"
type="button" title="&newtab.reset;"/>
xul:disablefastfind="true" xul:title="&newtab.pageTitle;">
<div id="newtab-scrollbox">
<div id="newtab-vertical-margin">
<div id="newtab-margin-top"/>
<div id="newtab-horizontal-margin">
<div class="newtab-side-margin"/>
<div id="newtab-grid">
<div class="newtab-row">
<div class="newtab-cell"/>
<div class="newtab-cell"/>
<div class="newtab-cell"/>
</div>
<div class="newtab-row">
<div class="newtab-cell"/>
<div class="newtab-cell"/>
<div class="newtab-cell"/>
</div>
<div class="newtab-row">
<div class="newtab-cell"/>
<div class="newtab-cell"/>
<div class="newtab-cell"/>
</div>
</div>
<div class="newtab-side-margin"/>
</div>
<ul id="grid">
<li class="cell"/><li class="cell"/><li class="cell"/>
<li class="cell"/><li class="cell"/><li class="cell"/>
<li class="cell"/><li class="cell"/><li class="cell"/>
</ul>
<div id="newtab-margin-bottom"/>
</div>
<input id="newtab-toggle" type="button"/>
</div>
<xul:script type="text/javascript;version=1.8"
src="chrome://browser/content/newtab/newTab.js"/>
</body>
</xul:vbox>
<xul:script type="text/javascript;version=1.8"
src="chrome://browser/content/newtab/newTab.js"/>
</xul:window>

View File

@@ -11,25 +11,24 @@
let gPage = {
/**
* Initializes the page.
* @param aToolbarSelector The query selector for the page toolbar.
* @param aGridSelector The query selector for the grid.
*/
init: function Page_init(aToolbarSelector, aGridSelector) {
gToolbar.init(aToolbarSelector);
this._gridSelector = aGridSelector;
init: function Page_init() {
// Add ourselves to the list of pages to receive notifications.
gAllPages.register(this);
// Listen for 'unload' to unregister this page.
function unload() { gAllPages.unregister(this); }
addEventListener("unload", unload.bind(this), false);
addEventListener("unload", this, false);
// Listen for toggle button clicks.
let button = document.getElementById("newtab-toggle");
button.addEventListener("click", this, false);
// Check if the new tab feature is enabled.
if (gAllPages.enabled)
let enabled = gAllPages.enabled;
if (enabled)
this._init();
else
this._updateAttributes(false);
this._updateAttributes(enabled);
},
/**
@@ -48,25 +47,9 @@ let gPage = {
* Updates the whole page and the grid when the storage has changed.
*/
update: function Page_update() {
this.updateModifiedFlag();
gGrid.refresh();
},
/**
* Checks if the page is modified and sets the CSS class accordingly
*/
updateModifiedFlag: function Page_updateModifiedFlag() {
let node = document.getElementById("toolbar-button-reset");
let modified = this._isModified();
if (modified)
node.setAttribute("modified", "true");
else
node.removeAttribute("modified");
this._updateTabIndices(gAllPages.enabled, modified);
},
/**
* Internally initializes the page. This runs only when/if the feature
* is/gets enabled.
@@ -78,29 +61,27 @@ let gPage = {
this._initialized = true;
gLinks.populateCache(function () {
// Check if the grid is modified.
this.updateModifiedFlag();
// Initialize and render the grid.
gGrid.init(this._gridSelector);
gGrid.init();
// Initialize the drop target shim.
gDropTargetShim.init();
#ifdef XP_MACOSX
// Workaround to prevent a delay on MacOSX due to a slow drop animation.
document.addEventListener("dragover", this.onDragOver, false);
document.addEventListener("drop", this.onDrop, false);
document.addEventListener("dragover", this, false);
document.addEventListener("drop", this, false);
#endif
}.bind(this));
},
/**
* Updates the 'page-disabled' attributes of the respective DOM nodes.
* @param aValue Whether to set or remove attributes.
* @param aValue Whether the New Tab Page is enabled or not.
*/
_updateAttributes: function Page_updateAttributes(aValue) {
let nodes = document.querySelectorAll("#grid, #scrollbox, #toolbar, .toolbar-button");
let selector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
let nodes = document.querySelectorAll(selector);
// Set the nodes' states.
for (let i = 0; i < nodes.length; i++) {
@@ -111,64 +92,32 @@ let gPage = {
node.setAttribute("page-disabled", "true");
}
this._updateTabIndices(aValue, this._isModified());
// Update the toggle button's title.
let toggle = document.getElementById("newtab-toggle");
toggle.setAttribute("title", newTabString(aValue ? "hide" : "show"));
},
/**
* Checks whether the page is modified.
* @return Whether the page is modified or not.
* Handles all page events.
*/
_isModified: function Page_isModified() {
// The page is considered modified only if sites have been removed.
return !gBlockedLinks.isEmpty();
},
/**
* Updates the tab indices of focusable elements.
* @param aEnabled Whether the page is currently enabled.
* @param aModified Whether the page is currently modified.
*/
_updateTabIndices: function Page_updateTabIndices(aEnabled, aModified) {
function setFocusable(aNode, aFocusable) {
if (aFocusable)
aNode.removeAttribute("tabindex");
else
aNode.setAttribute("tabindex", "-1");
}
// Sites and the 'hide' button are always focusable when the grid is shown.
let nodes = document.querySelectorAll(".site, #toolbar-button-hide");
for (let i = 0; i < nodes.length; i++)
setFocusable(nodes[i], aEnabled);
// The 'show' button is focusable when the grid is hidden.
let btnShow = document.getElementById("toolbar-button-show");
setFocusable(btnShow, !aEnabled);
// The 'reset' button is focusable when the grid is shown and modified.
let btnReset = document.getElementById("toolbar-button-reset");
setFocusable(btnReset, aEnabled && aModified);
},
/**
* Handles the 'dragover' event. Workaround to prevent a delay on MacOSX
* due to a slow drop animation.
* @param aEvent The 'dragover' event.
*/
onDragOver: function Page_onDragOver(aEvent) {
if (gDrag.isValid(aEvent) && gDrag.draggedSite)
aEvent.preventDefault();
},
/**
* Handles the 'drop' event. Workaround to prevent a delay on MacOSX due to
* a slow drop animation.
* @param aEvent The 'drop' event.
*/
onDrop: function Page_onDrop(aEvent) {
if (gDrag.isValid(aEvent) && gDrag.draggedSite) {
aEvent.preventDefault();
aEvent.stopPropagation();
handleEvent: function Page_handleEvent(aEvent) {
switch (aEvent.type) {
case "unload":
gAllPages.unregister(this);
break;
case "click":
gAllPages.enabled = !gAllPages.enabled;
break;
case "dragover":
if (gDrag.isValid(aEvent) && gDrag.draggedSite)
aEvent.preventDefault();
break;
case "drop":
if (gDrag.isValid(aEvent) && gDrag.draggedSite) {
aEvent.preventDefault();
aEvent.stopPropagation();
}
break;
}
}
};

View File

@@ -91,7 +91,6 @@ Site.prototype = {
} else {
gBlockedLinks.block(this._link);
gUpdater.updateGrid(aCallback);
gPage.updateModifiedFlag();
}
},
@@ -110,14 +109,14 @@ Site.prototype = {
* @param aPinned Whether this site is now pinned or unpinned.
*/
_updateAttributes: function (aPinned) {
let buttonPin = this._querySelector(".strip-button-pin");
let control = this._querySelector(".newtab-control-pin");
if (aPinned) {
this.node.setAttribute("pinned", true);
buttonPin.setAttribute("title", newTabString("unpin"));
control.setAttribute("pinned", true);
control.setAttribute("title", newTabString("unpin"));
} else {
this.node.removeAttribute("pinned");
buttonPin.setAttribute("title", newTabString("pin"));
control.removeAttribute("pinned");
control.setAttribute("title", newTabString("pin"));
}
},
@@ -126,32 +125,17 @@ Site.prototype = {
*/
_render: function Site_render() {
let title = this.title || this.url;
this.node.setAttribute("title", title);
this.node.setAttribute("href", this.url);
this._querySelector(".site-title").textContent = title;
let link = this._querySelector(".newtab-link");
link.setAttribute("title", title);
link.setAttribute("href", this.url);
this._querySelector(".newtab-title").textContent = title;
if (this.isPinned())
this._updateAttributes(true);
this._renderThumbnail();
},
/**
* Renders the site's thumbnail.
*/
_renderThumbnail: function Site_renderThumbnail() {
let img = this._querySelector(".site-img")
img.setAttribute("alt", this.title || this.url);
img.setAttribute("loading", "true");
// Wait until the image has loaded.
img.addEventListener("load", function onLoad() {
img.removeEventListener("load", onLoad, false);
img.removeAttribute("loading");
}, false);
// Set the thumbnail url.
img.setAttribute("src", PageThumbs.getThumbnailURL(this.url));
let thumbnailURL = PageThumbs.getThumbnailURL(this.url);
let thumbnail = this._querySelector(".newtab-thumbnail");
thumbnail.style.backgroundImage = "url(" + thumbnailURL + ")";
},
/**
@@ -159,56 +143,37 @@ Site.prototype = {
*/
_addEventHandlers: function Site_addEventHandlers() {
// Register drag-and-drop event handlers.
["DragStart", /*"Drag",*/ "DragEnd"].forEach(function (aType) {
let method = "_on" + aType;
this[method] = this[method].bind(this);
this._node.addEventListener(aType.toLowerCase(), this[method], false);
}, this);
this._node.addEventListener("dragstart", this, false);
this._node.addEventListener("dragend", this, false);
let self = this;
function pin(aEvent) {
if (aEvent)
aEvent.preventDefault();
if (self.isPinned())
self.unpin();
else
self.pin();
}
function block(aEvent) {
if (aEvent)
aEvent.preventDefault();
self.block();
}
this._querySelector(".strip-button-pin").addEventListener("click", pin, false);
this._querySelector(".strip-button-block").addEventListener("click", block, false);
let controls = this.node.querySelectorAll(".newtab-control");
for (let i = 0; i < controls.length; i++)
controls[i].addEventListener("click", this, false);
},
/**
* Event handler for the 'dragstart' event.
* @param aEvent The drag event.
* Handles all site events.
*/
_onDragStart: function Site_onDragStart(aEvent) {
gDrag.start(this, aEvent);
},
/**
* Event handler for the 'drag' event.
* @param aEvent The drag event.
*/
_onDrag: function Site_onDrag(aEvent) {
gDrag.drag(this, aEvent);
},
/**
* Event handler for the 'dragend' event.
* @param aEvent The drag event.
*/
_onDragEnd: function Site_onDragEnd(aEvent) {
gDrag.end(this, aEvent);
handleEvent: function Site_handleEvent(aEvent) {
switch (aEvent.type) {
case "click":
aEvent.preventDefault();
if (aEvent.target.classList.contains("newtab-control-block"))
this.block();
else if (this.isPinned())
this.unpin();
else
this.pin();
break;
case "dragstart":
gDrag.start(this, aEvent);
break;
case "drag":
gDrag.drag(this, aEvent);
break;
case "dragend":
gDrag.end(this, aEvent);
break;
}
}
};

View File

@@ -1,87 +0,0 @@
#ifdef 0
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#endif
/**
* This singleton represents the page's toolbar that allows to enable/disable
* the 'New Tab Page' feature and to reset the whole page.
*/
let gToolbar = {
/**
* Initializes the toolbar.
* @param aSelector The query selector of the toolbar.
*/
init: function Toolbar_init(aSelector) {
this._node = document.querySelector(aSelector);
let buttons = this._node.querySelectorAll("input");
// Listen for 'click' events on the toolbar buttons.
["show", "hide", "reset"].forEach(function (aType, aIndex) {
let self = this;
let button = buttons[aIndex];
let handler = function () self[aType]();
button.addEventListener("click", handler, false);
#ifdef XP_MACOSX
// Per default buttons lose focus after being clicked on Mac OS X.
// So when the URL bar has focus and a toolbar button is clicked the
// URL bar regains focus and the history pops up. We need to prevent
// that by explicitly removing its focus.
button.addEventListener("mousedown", function () {
window.focus();
}, false);
#endif
}, this);
},
/**
* Enables the 'New Tab Page' feature.
*/
show: function Toolbar_show() {
this._passButtonFocus("show", "hide");
gAllPages.enabled = true;
},
/**
* Disables the 'New Tab Page' feature.
*/
hide: function Toolbar_hide() {
this._passButtonFocus("hide", "show");
gAllPages.enabled = false;
},
/**
* Resets the whole page and forces it to re-render its content.
* @param aCallback The callback to call when the page has been reset.
*/
reset: function Toolbar_reset(aCallback) {
this._passButtonFocus("reset", "hide");
let node = gGrid.node;
// animate the page reset
gTransformation.fadeNodeOut(node, function () {
NewTabUtils.reset();
gLinks.populateCache(function () {
gAllPages.update();
// Without the setTimeout() we have a strange flicker.
setTimeout(function () gTransformation.fadeNodeIn(node, aCallback));
}, true);
});
},
/**
* Passes the focus from the current button to the next.
* @param aCurrent The button that currently has focus.
* @param aNext The button that is focused next.
*/
_passButtonFocus: function Toolbar_passButtonFocus(aCurrent, aNext) {
if (document.querySelector("#toolbar-button-" + aCurrent + ":-moz-focusring"))
document.getElementById("toolbar-button-" + aNext).focus();
}
};

View File

@@ -10,6 +10,24 @@
* convenience methods to work with a site's DOM node.
*/
let gTransformation = {
/**
* Returns the width of the left and top border of a cell. We need to take it
* into account when measuring and comparing site and cell positions.
*/
get _cellBorderWidths() {
let cstyle = window.getComputedStyle(gGrid.cells[0].node, null);
let widths = {
left: parseInt(cstyle.getPropertyValue("border-left-width")),
top: parseInt(cstyle.getPropertyValue("border-top-width"))
};
// Cache this value, overwrite the getter.
Object.defineProperty(this, "_cellBorderWidths",
{value: widths, enumerable: true});
return widths;
},
/**
* Gets a DOM node's position.
* @param aNode The DOM node.
@@ -80,6 +98,14 @@ let gTransformation = {
* @param aSite The site to freeze.
*/
freezeSitePosition: function Transformation_freezeSitePosition(aSite) {
if (this._isFrozen(aSite))
return;
let style = aSite.node.style;
let comp = getComputedStyle(aSite.node, null);
style.width = comp.getPropertyValue("width")
style.height = comp.getPropertyValue("height");
aSite.node.setAttribute("frozen", "true");
this.setSitePosition(aSite, this.getNodePosition(aSite.node));
},
@@ -89,8 +115,11 @@ let gTransformation = {
* @param aSite The site to unfreeze.
*/
unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) {
if (!this._isFrozen(aSite))
return;
let style = aSite.node.style;
style.left = style.top = "";
style.left = style.top = style.width = style.height = "";
aSite.node.removeAttribute("frozen");
},
@@ -117,8 +146,13 @@ let gTransformation = {
callback();
}
// We need to take the width of a cell's border into account.
targetPosition.left += this._cellBorderWidths.left;
targetPosition.top += this._cellBorderWidths.top;
// Nothing to do here if the positions already match.
if (currentPosition.equals(targetPosition)) {
if (currentPosition.left == targetPosition.left &&
currentPosition.top == targetPosition.top) {
finish();
} else {
this.setSitePosition(aSite, targetPosition);
@@ -222,5 +256,14 @@ let gTransformation = {
_moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) {
this.freezeSitePosition(aSite);
this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
},
/**
* Checks whether a site is currently frozen.
* @param aSite The site to check.
* @return Whether the given site is frozen.
*/
_isFrozen: function Transformation_isFrozen(aSite) {
return aSite.node.hasAttribute("frozen");
}
};

View File

@@ -18,7 +18,7 @@ const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
* Hint: This is the default value because the 'New Tab Page' is the only
* client for now.
*/
const THUMBNAIL_WIDTH = 201;
const THUMBNAIL_WIDTH = 400;
/**
* The default height for page thumbnails.
@@ -26,7 +26,7 @@ const THUMBNAIL_WIDTH = 201;
* Hint: This is the default value because the 'New Tab Page' is the only
* client for now.
*/
const THUMBNAIL_HEIGHT = 127;
const THUMBNAIL_HEIGHT = 225;
/**
* The default background color for page thumbnails.

View File

@@ -1,6 +1,2 @@
<!-- These strings are used in the about:newtab page -->
<!ENTITY newtab.pageTitle "New Tab">
<!ENTITY newtab.show "Show the New Tab Page">
<!ENTITY newtab.hide "Hide the New Tab Page">
<!ENTITY newtab.reset "Reset the New Tab Page">

View File

@@ -1,3 +1,5 @@
newtab.pin=Pin this site at its current position
newtab.unpin=Unpin this site
newtab.block=Remove this site
newtab.show=Show the new tab page
newtab.hide=Hide the new tab page

View File

@@ -1,148 +1,131 @@
#scrollbox {
padding-bottom: 18px;
background-color: #fff;
:root {
-moz-appearance: none;
background-color: transparent;
}
#body {
padding-top: 106px;
font-family: sans-serif;
/* SCROLLBOX */
#newtab-scrollbox:not([page-disabled]) {
background-color: rgb(229,229,229);
background-image: url(chrome://browser/skin/newtab/noise.png),
-moz-linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.2));
background-attachment: fixed;
}
.button {
/* TOGGLE */
#newtab-toggle {
width: 16px;
height: 16px;
padding: 0;
border: 0 none;
border: none;
background: -216px 0 transparent url(chrome://browser/skin/newtab/controls.png);
}
/* TOOLBAR */
#toolbar {
top: 8px;
right: 8px;
width: 13px;
height: 30px;
padding: 0;
margin: 0;
#newtab-toggle[page-disabled] {
background-position: -232px 0;
}
.toolbar-button {
background: transparent url(chrome://browser/skin/newtab/toolbar.png);
/* ROWS */
.newtab-row {
margin-bottom: 20px;
}
#toolbar-button-show {
width: 11px;
height: 11px;
background-position: -10px 0;
}
#toolbar-button-show:hover {
background-position: -10px -12px;
}
#toolbar-button-show:active {
background-position: -10px -24px;
}
#toolbar-button-hide {
width: 10px;
height: 10px;
}
#toolbar-button-hide:hover {
background-position: 0 -12px;
}
#toolbar-button-hide:active {
background-position: 0 -24px;
}
#toolbar-button-reset {
top: 17px;
width: 11px;
height: 12px;
}
#toolbar-button-reset {
background-position: -21px 0;
}
#toolbar-button-reset:hover {
background-position: -21px -12px;
}
#toolbar-button-reset:active {
background-position: -21px -24px;
}
/* GRID */
#grid {
padding: 1px;
margin: 0 auto;
.newtab-row:last-child {
margin-bottom: 0;
}
/* CELLS */
.cell {
outline: 1px dashed #ccc;
outline-offset: -1px;
.newtab-cell {
-moz-margin-end: 20px;
background-color: rgba(255,255,255,.2);
border: 1px solid;
border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
border-radius: 1px;
-moz-transition: border-color 100ms ease-out;
}
.newtab-cell:empty {
border: 1px dashed;
border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
}
.newtab-cell:last-child {
-moz-margin-end: 0;
}
.newtab-cell:hover:not(:empty) {
border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
}
/* SITES */
.site {
background-color: #ececec;
-moz-transition: 200ms ease-out;
-moz-transition-property: top, left, box-shadow, opacity;
}
.site[dragged] {
-moz-transition-property: box-shadow;
}
.site[ontop] {
box-shadow: 0 1px 4px #000;
outline: none;
}
/* SITE TITLE */
.site-title {
height: 2.4em;
width: 189px;
padding: 0 6px;
background-color: rgba(0,0,0,0.5);
border: solid transparent;
border-width: 6px 0;
color: #fff;
.newtab-site {
text-decoration: none;
line-height: 1.2em;
font-weight: 700;
-moz-transition-property: top, left, opacity, box-shadow, background-color;
}
/* SITE STRIP */
.site-strip {
padding: 4px 3px;
background-color: rgba(0,0,0,0.5);
.newtab-site:hover,
.newtab-site[dragged] {
box-shadow: 0 0 10px rgba(8,22,37,.3);
}
.strip-button {
width: 17px;
height: 17px;
background: transparent url(chrome://browser/skin/newtab/strip.png);
.newtab-site[dragged] {
-moz-transition-property: box-shadow, background-color;
background-color: rgb(242,242,242);
}
.strip-button-pin:hover {
background-position: 0 -17px;
/* THUMBNAILS */
.newtab-thumbnail {
background-origin: padding-box;
background-clip: padding-box;
background-repeat: no-repeat;
background-size: cover;
}
.strip-button-pin:active,
.site[pinned] .strip-button-pin {
background-position: 0 -34px;
/* TITLES */
.newtab-title {
padding: 0 8px;
background-color: rgba(248,249,251,.95);
color: #1f364c;
font-size: 12px;
line-height: 24px;
}
.strip-button-block {
background-position: -17px 0;
/* CONTROLS */
.newtab-control {
width: 24px;
height: 24px;
padding: 1px 2px 3px;
border: none;
background: transparent url(chrome://browser/skin/newtab/controls.png);
}
.strip-button-block:hover {
background-position: -17px -17px;
.newtab-control-pin:hover {
background-position: -24px 0;
}
.strip-button-block:active {
background-position: -17px -34px;
.newtab-control-pin:active {
background-position: -48px 0;
}
.newtab-control-pin[pinned] {
background-position: -72px 0;
}
.newtab-control-pin[pinned]:hover {
background-position: -96px 0;
}
.newtab-control-pin[pinned]:active {
background-position: -120px 0;
}
.newtab-control-block {
background-position: -144px 0;
}
.newtab-control-block:hover {
background-position: -168px 0;
}
.newtab-control-block:active {
background-position: -192px 0;
}

View File

@@ -1,147 +1,131 @@
#scrollbox {
padding-bottom: 18px;
background-color: #fff;
:root {
-moz-appearance: none;
background-color: transparent;
}
#body {
padding-top: 106px;
font-family: sans-serif;
/* SCROLLBOX */
#newtab-scrollbox:not([page-disabled]) {
background-color: rgb(229,229,229);
background-image: url(chrome://browser/skin/newtab/noise.png),
-moz-linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.2));
background-attachment: fixed;
}
.button {
/* TOGGLE */
#newtab-toggle {
width: 16px;
height: 16px;
padding: 0;
border: 0 none;
border: none;
background: -216px 0 transparent url(chrome://browser/skin/newtab/controls.png);
}
/* TOOLBAR */
#toolbar {
top: 8px;
right: 8px;
width: 13px;
height: 30px;
padding: 0;
margin: 0;
#newtab-toggle[page-disabled] {
background-position: -232px 0;
}
.toolbar-button {
background: transparent url(chrome://browser/skin/newtab/toolbar.png);
/* ROWS */
.newtab-row {
margin-bottom: 20px;
}
#toolbar-button-show {
width: 11px;
height: 11px;
background-position: -10px 0;
}
#toolbar-button-show:hover {
background-position: -10px -12px;
}
#toolbar-button-show:active {
background-position: -10px -24px;
}
#toolbar-button-hide {
width: 10px;
height: 10px;
}
#toolbar-button-hide:hover {
background-position: 0 -12px;
}
#toolbar-button-hide:active {
background-position: 0 -24px;
}
#toolbar-button-reset {
top: 17px;
width: 11px;
height: 12px;
}
#toolbar-button-reset {
background-position: -21px 0;
}
#toolbar-button-reset:hover {
background-position: -21px -12px;
}
#toolbar-button-reset:active {
background-position: -21px -24px;
}
/* GRID */
#grid {
padding: 1px;
margin: 0 auto;
.newtab-row:last-child {
margin-bottom: 0;
}
/* CELLS */
.cell {
outline: 1px dashed #ccc;
outline-offset: -1px;
.newtab-cell {
-moz-margin-end: 20px;
background-color: rgba(255,255,255,.2);
border: 1px solid;
border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
border-radius: 1px;
-moz-transition: border-color 100ms ease-out;
}
.newtab-cell:empty {
border: 1px dashed;
border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
}
.newtab-cell:last-child {
-moz-margin-end: 0;
}
.newtab-cell:hover:not(:empty) {
border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
}
/* SITES */
.site {
background-color: #ececec;
-moz-transition: 200ms ease-out;
-moz-transition-property: top, left, box-shadow, opacity;
}
.site[dragged] {
-moz-transition-property: box-shadow;
}
.site[ontop] {
box-shadow: 0 1px 4px #000;
}
/* SITE TITLE */
.site-title {
height: 2.4em;
width: 189px;
padding: 0 6px;
background-color: rgba(0,0,0,0.5);
border: solid transparent;
border-width: 6px 0;
color: #fff;
.newtab-site {
text-decoration: none;
line-height: 1.2em;
font-weight: 700;
-moz-transition-property: top, left, opacity, box-shadow, background-color;
}
/* SITE STRIP */
.site-strip {
padding: 4px 3px;
background-color: rgba(0,0,0,0.5);
.newtab-site:hover,
.newtab-site[dragged] {
box-shadow: 0 0 10px rgba(8,22,37,.3);
}
.strip-button {
width: 17px;
height: 17px;
background: transparent url(chrome://browser/skin/newtab/strip.png);
.newtab-site[dragged] {
-moz-transition-property: box-shadow, background-color;
background-color: rgb(242,242,242);
}
.strip-button-pin:hover {
background-position: 0 -17px;
/* THUMBNAILS */
.newtab-thumbnail {
background-origin: padding-box;
background-clip: padding-box;
background-repeat: no-repeat;
background-size: cover;
}
.strip-button-pin:active,
.site[pinned] .strip-button-pin {
background-position: 0 -34px;
/* TITLES */
.newtab-title {
padding: 0 8px;
background-color: rgba(248,249,251,.95);
color: #1f364c;
font-size: 12px;
line-height: 24px;
}
.strip-button-block {
background-position: -17px 0;
/* CONTROLS */
.newtab-control {
width: 24px;
height: 24px;
padding: 1px 2px 3px;
border: none;
background: transparent url(chrome://browser/skin/newtab/controls.png);
}
.strip-button-block:hover {
background-position: -17px -17px;
.newtab-control-pin:hover {
background-position: -24px 0;
}
.strip-button-block:active {
background-position: -17px -34px;
.newtab-control-pin:active {
background-position: -48px 0;
}
.newtab-control-pin[pinned] {
background-position: -72px 0;
}
.newtab-control-pin[pinned]:hover {
background-position: -96px 0;
}
.newtab-control-pin[pinned]:active {
background-position: -120px 0;
}
.newtab-control-block {
background-position: -144px 0;
}
.newtab-control-block:hover {
background-position: -168px 0;
}
.newtab-control-block:active {
background-position: -192px 0;
}

View File

@@ -1,148 +1,131 @@
#scrollbox {
padding-bottom: 18px;
background-color: #fff;
:root {
-moz-appearance: none;
background-color: transparent;
}
#body {
padding-top: 106px;
font-family: sans-serif;
/* SCROLLBOX */
#newtab-scrollbox:not([page-disabled]) {
background-color: rgb(229,229,229);
background-image: url(chrome://browser/skin/newtab/noise.png),
-moz-linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.2));
background-attachment: fixed;
}
.button {
/* TOGGLE */
#newtab-toggle {
width: 16px;
height: 16px;
padding: 0;
border: 0 none;
border: none;
background: -216px 0 transparent url(chrome://browser/skin/newtab/controls.png);
}
/* TOOLBAR */
#toolbar {
top: 8px;
right: 8px;
width: 13px;
height: 30px;
padding: 0;
margin: 0;
#newtab-toggle[page-disabled] {
background-position: -232px 0;
}
.toolbar-button {
background: transparent url(chrome://browser/skin/newtab/toolbar.png);
/* ROWS */
.newtab-row {
margin-bottom: 20px;
}
#toolbar-button-show {
width: 11px;
height: 11px;
background-position: -10px 0;
}
#toolbar-button-show:hover {
background-position: -10px -12px;
}
#toolbar-button-show:active {
background-position: -10px -24px;
}
#toolbar-button-hide {
width: 10px;
height: 10px;
}
#toolbar-button-hide:hover {
background-position: 0 -12px;
}
#toolbar-button-hide:active {
background-position: 0 -24px;
}
#toolbar-button-reset {
top: 17px;
width: 11px;
height: 12px;
}
#toolbar-button-reset {
background-position: -21px 0;
}
#toolbar-button-reset:hover {
background-position: -21px -12px;
}
#toolbar-button-reset:active {
background-position: -21px -24px;
}
/* GRID */
#grid {
padding: 1px;
margin: 0 auto;
.newtab-row:last-child {
margin-bottom: 0;
}
/* CELLS */
.cell {
outline: 1px dashed #ccc;
outline-offset: -1px;
.newtab-cell {
-moz-margin-end: 20px;
background-color: rgba(255,255,255,.2);
border: 1px solid;
border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
border-radius: 1px;
-moz-transition: border-color 100ms ease-out;
}
.newtab-cell:empty {
border: 1px dashed;
border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
}
.newtab-cell:last-child {
-moz-margin-end: 0;
}
.newtab-cell:hover:not(:empty) {
border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
}
/* SITES */
.site {
background-color: #ececec;
-moz-transition: 200ms ease-out;
-moz-transition-property: top, left, box-shadow, opacity;
}
.site[dragged] {
-moz-transition-property: box-shadow;
}
.site[ontop] {
box-shadow: 0 1px 4px #000;
outline: none;
}
/* SITE TITLE */
.site-title {
height: 2.4em;
width: 189px;
padding: 0 6px;
background-color: rgba(0,0,0,0.5);
border: solid transparent;
border-width: 6px 0;
color: #fff;
.newtab-site {
text-decoration: none;
line-height: 1.2em;
font-weight: 700;
-moz-transition-property: top, left, opacity, box-shadow, background-color;
}
/* SITE STRIP */
.site-strip {
padding: 4px 3px;
background-color: rgba(0,0,0,0.5);
.newtab-site:hover,
.newtab-site[dragged] {
box-shadow: 0 0 10px rgba(8,22,37,.3);
}
.strip-button {
width: 17px;
height: 17px;
background: transparent url(chrome://browser/skin/newtab/strip.png);
.newtab-site[dragged] {
-moz-transition-property: box-shadow, background-color;
background-color: rgb(242,242,242);
}
.strip-button-pin:hover {
background-position: 0 -17px;
/* THUMBNAILS */
.newtab-thumbnail {
background-origin: padding-box;
background-clip: padding-box;
background-repeat: no-repeat;
background-size: cover;
}
.strip-button-pin:active,
.site[pinned] .strip-button-pin {
background-position: 0 -34px;
/* TITLES */
.newtab-title {
padding: 0 8px;
background-color: rgba(248,249,251,.95);
color: #1f364c;
font-size: 12px;
line-height: 24px;
}
.strip-button-block {
background-position: -17px 0;
/* CONTROLS */
.newtab-control {
width: 24px;
height: 24px;
padding: 1px 2px 3px;
border: none;
background: transparent url(chrome://browser/skin/newtab/controls.png);
}
.strip-button-block:hover {
background-position: -17px -17px;
.newtab-control-pin:hover {
background-position: -24px 0;
}
.strip-button-block:active {
background-position: -17px -34px;
.newtab-control-pin:active {
background-position: -48px 0;
}
.newtab-control-pin[pinned] {
background-position: -72px 0;
}
.newtab-control-pin[pinned]:hover {
background-position: -96px 0;
}
.newtab-control-pin[pinned]:active {
background-position: -120px 0;
}
.newtab-control-block {
background-position: -144px 0;
}
.newtab-control-block:hover {
background-position: -168px 0;
}
.newtab-control-block:active {
background-position: -192px 0;
}