diff options
Diffstat (limited to 'components/newtab')
-rw-r--r-- | components/newtab/cells.js | 126 | ||||
-rw-r--r-- | components/newtab/drag.js | 151 | ||||
-rw-r--r-- | components/newtab/dragDataHelper.js | 22 | ||||
-rw-r--r-- | components/newtab/drop.js | 150 | ||||
-rw-r--r-- | components/newtab/dropPreview.js | 222 | ||||
-rw-r--r-- | components/newtab/dropTargetShim.js | 232 | ||||
-rw-r--r-- | components/newtab/grid.js | 175 | ||||
-rw-r--r-- | components/newtab/jar.mn | 8 | ||||
-rw-r--r-- | components/newtab/moz.build | 8 | ||||
-rw-r--r-- | components/newtab/newTab.css | 349 | ||||
-rw-r--r-- | components/newtab/newTab.js | 69 | ||||
-rw-r--r-- | components/newtab/newTab.xhtml | 61 | ||||
-rw-r--r-- | components/newtab/page.js | 244 | ||||
-rw-r--r-- | components/newtab/search.js | 134 | ||||
-rw-r--r-- | components/newtab/sites.js | 353 | ||||
-rw-r--r-- | components/newtab/transformations.js | 270 | ||||
-rw-r--r-- | components/newtab/undo.js | 116 | ||||
-rw-r--r-- | components/newtab/updater.js | 177 |
18 files changed, 0 insertions, 2867 deletions
diff --git a/components/newtab/cells.js b/components/newtab/cells.js deleted file mode 100644 index 47d4ef5..0000000 --- a/components/newtab/cells.js +++ /dev/null @@ -1,126 +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 class manages a cell's DOM node (not the actually cell content, a site). - * It's mostly read-only, i.e. all manipulation of both position and content - * aren't handled here. - */ -function Cell(aGrid, aNode) { - this._grid = aGrid; - this._node = aNode; - this._node._newtabCell = this; - - // Register drag-and-drop event handlers. - ["dragenter", "dragover", "dragexit", "drop"].forEach(function (aType) { - this._node.addEventListener(aType, this, false); - }, this); -} - -Cell.prototype = { - /** - * The grid. - */ - _grid: null, - - /** - * The cell's DOM node. - */ - get node() { return this._node; }, - - /** - * The cell's offset in the grid. - */ - get index() { - let index = this._grid.cells.indexOf(this); - - // Cache this value, overwrite the getter. - Object.defineProperty(this, "index", {value: index, enumerable: true}); - - return index; - }, - - /** - * The previous cell in the grid. - */ - get previousSibling() { - let prev = this.node.previousElementSibling; - prev = prev && prev._newtabCell; - - // Cache this value, overwrite the getter. - Object.defineProperty(this, "previousSibling", {value: prev, enumerable: true}); - - return prev; - }, - - /** - * The next cell in the grid. - */ - get nextSibling() { - let next = this.node.nextElementSibling; - next = next && next._newtabCell; - - // Cache this value, overwrite the getter. - Object.defineProperty(this, "nextSibling", {value: next, enumerable: true}); - - return next; - }, - - /** - * The site contained in the cell, if any. - */ - get site() { - let firstChild = this.node.firstElementChild; - return firstChild && firstChild._newtabSite; - }, - - /** - * Checks whether the cell contains a pinned site. - * @return Whether the cell contains a pinned site. - */ - containsPinnedSite: function Cell_containsPinnedSite() { - let site = this.site; - return site && site.isPinned(); - }, - - /** - * Checks whether the cell contains a site (is empty). - * @return Whether the cell is empty. - */ - isEmpty: function Cell_isEmpty() { - return !this.site; - }, - - /** - * Handles all cell events. - */ - handleEvent: function Cell_handleEvent(aEvent) { - // We're not responding to external drag/drop events - // when our parent window is in private browsing mode. - if (inPrivateBrowsingMode() && !gDrag.draggedSite) - return; - - if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent)) - return; - - 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; - } - } -}; diff --git a/components/newtab/drag.js b/components/newtab/drag.js deleted file mode 100644 index e3928eb..0000000 --- a/components/newtab/drag.js +++ /dev/null @@ -1,151 +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 implements site dragging functionality. - */ -var gDrag = { - /** - * The site offset to the drag start point. - */ - _offsetX: null, - _offsetY: null, - - /** - * The site that is dragged. - */ - _draggedSite: null, - get draggedSite() { return this._draggedSite; }, - - /** - * The cell width/height at the point the drag started. - */ - _cellWidth: null, - _cellHeight: null, - get cellWidth() { return this._cellWidth; }, - get cellHeight() { return this._cellHeight; }, - - /** - * Start a new drag operation. - * @param aSite The site that's being dragged. - * @param aEvent The 'dragstart' event. - */ - start: function Drag_start(aSite, aEvent) { - this._draggedSite = aSite; - - // Mark nodes as being dragged. - let selector = ".newtab-site, .newtab-control, .newtab-thumbnail"; - let parentCell = aSite.node.parentNode; - let nodes = parentCell.querySelectorAll(selector); - for (let i = 0; i < nodes.length; i++) - nodes[i].setAttribute("dragged", "true"); - - parentCell.setAttribute("dragged", "true"); - - this._setDragData(aSite, aEvent); - - // Store the cursor offset. - let node = aSite.node; - let rect = node.getBoundingClientRect(); - this._offsetX = aEvent.clientX - rect.left; - this._offsetY = aEvent.clientY - rect.top; - - // Store the cell dimensions. - let cellNode = aSite.cell.node; - this._cellWidth = cellNode.offsetWidth; - this._cellHeight = cellNode.offsetHeight; - - gTransformation.freezeSitePosition(aSite); - }, - - /** - * Handles the 'drag' event. - * @param aSite The site that's being dragged. - * @param aEvent The 'drag' event. - */ - drag: function Drag_drag(aSite, aEvent) { - // Get the viewport size. - let {clientWidth, clientHeight} = document.documentElement; - - // We'll want a padding of 5px. - let border = 5; - - // Enforce minimum constraints to keep the drag image inside the window. - let left = Math.max(scrollX + aEvent.clientX - this._offsetX, border); - let top = Math.max(scrollY + aEvent.clientY - this._offsetY, border); - - // Enforce maximum constraints to keep the drag image inside the window. - left = Math.min(left, scrollX + clientWidth - this.cellWidth - border); - top = Math.min(top, scrollY + clientHeight - this.cellHeight - border); - - // Update the drag image's position. - gTransformation.setSitePosition(aSite, {left: left, top: top}); - }, - - /** - * Ends the current drag operation. - * @param aSite The site that's being dragged. - * @param aEvent The 'dragend' event. - */ - end: function Drag_end(aSite, aEvent) { - let nodes = gGrid.node.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}); - - this._draggedSite = null; - }, - - /** - * Checks whether we're responsible for a given drag event. - * @param aEvent The drag event to check. - * @return Whether we should handle this drag and drop operation. - */ - isValid: function Drag_isValid(aEvent) { - let link = gDragDataHelper.getLinkFromDragEvent(aEvent); - - // Check that the drag data is non-empty. - // Can happen when dragging places folders. - if (!link || !link.url) { - return false; - } - - // Check that we're not accepting URLs which would inherit the caller's - // principal (such as javascript: or data:). - return gLinkChecker.checkLoadURI(link.url); - }, - - /** - * Initializes the drag data for the current drag operation. - * @param aSite The site that's being dragged. - * @param aEvent The 'dragstart' event. - */ - _setDragData: function Drag_setDragData(aSite, aEvent) { - let {url, title} = aSite; - - let dt = aEvent.dataTransfer; - dt.mozCursor = "default"; - dt.effectAllowed = "move"; - dt.setData("text/plain", url); - dt.setData("text/uri-list", url); - dt.setData("text/x-moz-url", url + "\n" + title); - dt.setData("text/html", "<a href=\"" + url + "\">" + url + "</a>"); - - // 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("newtab-drag"); - let scrollbox = document.getElementById("newtab-vertical-margin"); - 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(() => scrollbox.removeChild(dragElement), 0); - } -}; diff --git a/components/newtab/dragDataHelper.js b/components/newtab/dragDataHelper.js deleted file mode 100644 index 675ff26..0000000 --- a/components/newtab/dragDataHelper.js +++ /dev/null @@ -1,22 +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 - -var gDragDataHelper = { - get mimeType() { - return "text/x-moz-url"; - }, - - getLinkFromDragEvent: function DragDataHelper_getLinkFromDragEvent(aEvent) { - let dt = aEvent.dataTransfer; - if (!dt || !dt.types.includes(this.mimeType)) { - return null; - } - - let data = dt.getData(this.mimeType) || ""; - let [url, title] = data.split(/[\r\n]+/); - return {url: url, title: title}; - } -}; diff --git a/components/newtab/drop.js b/components/newtab/drop.js deleted file mode 100644 index 7486524..0000000 --- a/components/newtab/drop.js +++ /dev/null @@ -1,150 +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 - -// A little delay that prevents the grid from being too sensitive when dragging -// sites around. -const DELAY_REARRANGE_MS = 100; - -/** - * This singleton implements site dropping functionality. - */ -var gDrop = { - /** - * The last drop target. - */ - _lastDropTarget: null, - - /** - * Handles the 'dragenter' event. - * @param aCell The drop target cell. - */ - enter: function Drop_enter(aCell) { - this._delayedRearrange(aCell); - }, - - /** - * Handles the 'dragexit' event. - * @param aCell The drop target cell. - * @param aEvent The 'dragexit' event. - */ - exit: function Drop_exit(aCell, aEvent) { - if (aEvent.dataTransfer && !aEvent.dataTransfer.mozUserCancelled) { - this._delayedRearrange(); - } else { - // The drag operation has been cancelled. - this._cancelDelayedArrange(); - this._rearrange(); - } - }, - - /** - * Handles the 'drop' event. - * @param aCell The drop target cell. - * @param aEvent The 'dragexit' event. - */ - drop: function Drop_drop(aCell, aEvent) { - // The cell that is the drop target could contain a pinned site. We need - // to find out where that site has gone and re-pin it there. - if (aCell.containsPinnedSite()) - this._repinSitesAfterDrop(aCell); - - // Pin the dragged or insert the new site. - this._pinDraggedSite(aCell, aEvent); - - this._cancelDelayedArrange(); - - // Update the grid and move all sites to their new places. - gUpdater.updateGrid(); - }, - - /** - * Re-pins all pinned sites in their (new) positions. - * @param aCell The drop target cell. - */ - _repinSitesAfterDrop: function Drop_repinSitesAfterDrop(aCell) { - let sites = gDropPreview.rearrange(aCell); - - // Filter out pinned sites. - let pinnedSites = sites.filter(function (aSite) { - return aSite && aSite.isPinned(); - }); - - // Re-pin all shifted pinned cells. - pinnedSites.forEach(aSite => aSite.pin(sites.indexOf(aSite))); - }, - - /** - * Pins the dragged site in its new place. - * @param aCell The drop target cell. - * @param aEvent The 'dragexit' event. - */ - _pinDraggedSite: function Drop_pinDraggedSite(aCell, aEvent) { - let index = aCell.index; - let draggedSite = gDrag.draggedSite; - - if (draggedSite) { - // Pin the dragged site at its new place. - if (aCell != draggedSite.cell) - draggedSite.pin(index); - } else { - let link = gDragDataHelper.getLinkFromDragEvent(aEvent); - if (link) { - // A new link was dragged onto the grid. Create it by pinning its URL. - gPinnedLinks.pin(link, index); - - // Make sure the newly added link is not blocked. - gBlockedLinks.unblock(link); - } - } - }, - - /** - * Time a rearrange with a little delay. - * @param aCell The drop target cell. - */ - _delayedRearrange: function Drop_delayedRearrange(aCell) { - // The last drop target didn't change so there's no need to re-arrange. - if (this._lastDropTarget == aCell) - return; - - let self = this; - - function callback() { - self._rearrangeTimeout = null; - self._rearrange(aCell); - } - - this._cancelDelayedArrange(); - this._rearrangeTimeout = setTimeout(callback, DELAY_REARRANGE_MS); - - // Store the last drop target. - this._lastDropTarget = aCell; - }, - - /** - * Cancels a timed rearrange, if any. - */ - _cancelDelayedArrange: function Drop_cancelDelayedArrange() { - if (this._rearrangeTimeout) { - clearTimeout(this._rearrangeTimeout); - this._rearrangeTimeout = null; - } - }, - - /** - * Rearrange all sites in the grid depending on the current drop target. - * @param aCell The drop target cell. - */ - _rearrange: function Drop_rearrange(aCell) { - let sites = gGrid.sites; - - // We need to rearrange the grid only if there's a current drop target. - if (aCell) - sites = gDropPreview.rearrange(aCell); - - gTransformation.rearrangeSites(sites, {unfreeze: !aCell}); - } -}; diff --git a/components/newtab/dropPreview.js b/components/newtab/dropPreview.js deleted file mode 100644 index fd7587a..0000000 --- a/components/newtab/dropPreview.js +++ /dev/null @@ -1,222 +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 provides the ability to re-arrange the current grid to - * indicate the transformation that results from dropping a cell at a certain - * position. - */ -var gDropPreview = { - /** - * Rearranges the sites currently contained in the grid when a site would be - * dropped onto the given cell. - * @param aCell The drop target cell. - * @return The re-arranged array of sites. - */ - rearrange: function DropPreview_rearrange(aCell) { - let sites = gGrid.sites; - - // Insert the dragged site into the current grid. - this._insertDraggedSite(sites, aCell); - - // After the new site has been inserted we need to correct the positions - // of all pinned tabs that have been moved around. - this._repositionPinnedSites(sites, aCell); - - return sites; - }, - - /** - * Inserts the currently dragged site into the given array of sites. - * @param aSites The array of sites to insert into. - * @param aCell The drop target cell. - */ - _insertDraggedSite: function DropPreview_insertDraggedSite(aSites, aCell) { - let dropIndex = aCell.index; - let draggedSite = gDrag.draggedSite; - - // We're currently dragging a site. - if (draggedSite) { - let dragCell = draggedSite.cell; - let dragIndex = dragCell.index; - - // Move the dragged site into its new position. - if (dragIndex != dropIndex) { - aSites.splice(dragIndex, 1); - aSites.splice(dropIndex, 0, draggedSite); - } - // We're handling an external drag item. - } else { - aSites.splice(dropIndex, 0, null); - } - }, - - /** - * Correct the position of all pinned sites that might have been moved to - * different positions after the dragged site has been inserted. - * @param aSites The array of sites containing the dragged site. - * @param aCell The drop target cell. - */ - _repositionPinnedSites: - function DropPreview_repositionPinnedSites(aSites, aCell) { - - // Collect all pinned sites. - let pinnedSites = this._filterPinnedSites(aSites, aCell); - - // Correct pinned site positions. - pinnedSites.forEach(function (aSite) { - aSites[aSites.indexOf(aSite)] = aSites[aSite.cell.index]; - aSites[aSite.cell.index] = aSite; - }, this); - - // There might be a pinned cell that got pushed out of the grid, try to - // sneak it in by removing a lower-priority cell. - if (this._hasOverflowedPinnedSite(aSites, aCell)) - this._repositionOverflowedPinnedSite(aSites, aCell); - }, - - /** - * Filter pinned sites out of the grid that are still on their old positions - * and have not moved. - * @param aSites The array of sites to filter. - * @param aCell The drop target cell. - * @return The filtered array of sites. - */ - _filterPinnedSites: function DropPreview_filterPinnedSites(aSites, aCell) { - let draggedSite = gDrag.draggedSite; - - // When dropping on a cell that contains a pinned site make sure that all - // pinned cells surrounding the drop target are moved as well. - let range = this._getPinnedRange(aCell); - - return aSites.filter(function (aSite, aIndex) { - // The site must be valid, pinned and not the dragged site. - if (!aSite || aSite == draggedSite || !aSite.isPinned()) - return false; - - let index = aSite.cell.index; - - // If it's not in the 'pinned range' it's a valid pinned site. - return (index > range.end || index < range.start); - }); - }, - - /** - * Determines the range of pinned sites surrounding the drop target cell. - * @param aCell The drop target cell. - * @return The range of pinned cells. - */ - _getPinnedRange: function DropPreview_getPinnedRange(aCell) { - let dropIndex = aCell.index; - let range = {start: dropIndex, end: dropIndex}; - - // We need a pinned range only when dropping on a pinned site. - if (aCell.containsPinnedSite()) { - let links = gPinnedLinks.links; - - // Find all previous siblings of the drop target that are pinned as well. - while (range.start && links[range.start - 1]) - range.start--; - - let maxEnd = links.length - 1; - - // Find all next siblings of the drop target that are pinned as well. - while (range.end < maxEnd && links[range.end + 1]) - range.end++; - } - - return range; - }, - - /** - * Checks if the given array of sites contains a pinned site that has - * been pushed out of the grid. - * @param aSites The array of sites to check. - * @param aCell The drop target cell. - * @return Whether there is an overflowed pinned cell. - */ - _hasOverflowedPinnedSite: - function DropPreview_hasOverflowedPinnedSite(aSites, aCell) { - - // If the drop target isn't pinned there's no way a pinned site has been - // pushed out of the grid so we can just exit here. - if (!aCell.containsPinnedSite()) - return false; - - let cells = gGrid.cells; - - // No cells have been pushed out of the grid, nothing to do here. - if (aSites.length <= cells.length) - return false; - - let overflowedSite = aSites[cells.length]; - - // Nothing to do if the site that got pushed out of the grid is not pinned. - return (overflowedSite && overflowedSite.isPinned()); - }, - - /** - * We have a overflowed pinned site that we need to re-position so that it's - * visible again. We try to find a lower-priority cell (empty or containing - * an unpinned site) that we can move it to. - * @param aSites The array of sites. - * @param aCell The drop target cell. - */ - _repositionOverflowedPinnedSite: - function DropPreview_repositionOverflowedPinnedSite(aSites, aCell) { - - // Try to find a lower-priority cell (empty or containing an unpinned site). - let index = this._indexOfLowerPrioritySite(aSites, aCell); - - if (index > -1) { - let cells = gGrid.cells; - let dropIndex = aCell.index; - - // Move all pinned cells to their new positions to let the overflowed - // site fit into the grid. - for (let i = index + 1, lastPosition = index; i < aSites.length; i++) { - if (i != dropIndex) { - aSites[lastPosition] = aSites[i]; - lastPosition = i; - } - } - - // Finally, remove the overflowed site from its previous position. - aSites.splice(cells.length, 1); - } - }, - - /** - * Finds the index of the last cell that is empty or contains an unpinned - * site. These are considered to be of a lower priority. - * @param aSites The array of sites. - * @param aCell The drop target cell. - * @return The cell's index. - */ - _indexOfLowerPrioritySite: - function DropPreview_indexOfLowerPrioritySite(aSites, aCell) { - - let cells = gGrid.cells; - let dropIndex = aCell.index; - - // Search (beginning with the last site in the grid) for a site that is - // empty or unpinned (an thus lower-priority) and can be pushed out of the - // grid instead of the pinned site. - for (let i = cells.length - 1; i >= 0; i--) { - // The cell that is our drop target is not a good choice. - if (i == dropIndex) - continue; - - let site = aSites[i]; - - // We can use the cell only if it's empty or the site is un-pinned. - if (!site || !site.isPinned()) - return i; - } - - return -1; - } -}; diff --git a/components/newtab/dropTargetShim.js b/components/newtab/dropTargetShim.js deleted file mode 100644 index 57a97fa..0000000 --- a/components/newtab/dropTargetShim.js +++ /dev/null @@ -1,232 +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 provides a custom drop target detection. We need this because - * the default DnD target detection relies on the cursor's position. We want - * to pick a drop target based on the dragged site's position. - */ -var gDropTargetShim = { - /** - * Cache for the position of all cells, cleaned after drag finished. - */ - _cellPositions: null, - - /** - * The last drop target that was hovered. - */ - _lastDropTarget: null, - - /** - * Initializes the drop target shim. - */ - init: function () { - gGrid.node.addEventListener("dragstart", this, true); - }, - - /** - * Add all event listeners needed during a drag operation. - */ - _addEventListeners: function () { - gGrid.node.addEventListener("dragend", this); - - let docElement = document.documentElement; - docElement.addEventListener("dragover", this); - docElement.addEventListener("dragenter", this); - docElement.addEventListener("drop", this); - }, - - /** - * Remove all event listeners that were needed during a drag operation. - */ - _removeEventListeners: function () { - gGrid.node.removeEventListener("dragend", this); - - let docElement = document.documentElement; - docElement.removeEventListener("dragover", this); - docElement.removeEventListener("dragenter", this); - docElement.removeEventListener("drop", this); - }, - - /** - * Handles all shim events. - */ - handleEvent: function (aEvent) { - switch (aEvent.type) { - case "dragstart": - this._dragstart(aEvent); - break; - case "dragenter": - aEvent.preventDefault(); - break; - case "dragover": - this._dragover(aEvent); - break; - case "drop": - this._drop(aEvent); - break; - case "dragend": - this._dragend(aEvent); - break; - } - }, - - /** - * Handles the 'dragstart' event. - * @param aEvent The 'dragstart' event. - */ - _dragstart: function (aEvent) { - if (aEvent.target.classList.contains("newtab-link")) { - gGrid.lock(); - this._addEventListeners(); - } - }, - - /** - * Handles the 'dragover' event. - * @param aEvent The 'dragover' event. - */ - _dragover: function (aEvent) { - // XXX bug 505521 - Use the dragover event to retrieve the - // current mouse coordinates while dragging. - let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode; - gDrag.drag(sourceNode._newtabSite, aEvent); - - // Find the current drop target, if there's one. - this._updateDropTarget(aEvent); - - // If we have a valid drop target, - // let the drag-and-drop service know. - if (this._lastDropTarget) { - aEvent.preventDefault(); - } - }, - - /** - * Handles the 'drop' event. - * @param aEvent The 'drop' event. - */ - _drop: function (aEvent) { - // We're accepting all drops. - aEvent.preventDefault(); - - // remember that drop event was seen, this explicitly - // assumes that drop event preceeds dragend event - this._dropSeen = true; - - // Make sure to determine the current drop target - // in case the dragover event hasn't been fired. - this._updateDropTarget(aEvent); - - // A site was successfully dropped. - this._dispatchEvent(aEvent, "drop", this._lastDropTarget); - }, - - /** - * Handles the 'dragend' event. - * @param aEvent The 'dragend' event. - */ - _dragend: function (aEvent) { - if (this._lastDropTarget) { - if (aEvent.dataTransfer.mozUserCancelled || !this._dropSeen) { - // The drag operation was cancelled or no drop event was generated - this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget); - this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget); - } - - // Clean up. - this._lastDropTarget = null; - this._cellPositions = null; - } - - this._dropSeen = false; - gGrid.unlock(); - this._removeEventListeners(); - }, - - /** - * Tries to find the current drop target and will fire - * appropriate dragenter, dragexit, and dragleave events. - * @param aEvent The current drag event. - */ - _updateDropTarget: function (aEvent) { - // Let's see if we find a drop target. - let target = this._findDropTarget(aEvent); - - if (target != this._lastDropTarget) { - if (this._lastDropTarget) - // We left the last drop target. - this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget); - - if (target) - // We're now hovering a (new) drop target. - this._dispatchEvent(aEvent, "dragenter", target); - - if (this._lastDropTarget) - // We left the last drop target. - this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget); - - this._lastDropTarget = target; - } - }, - - /** - * Determines the current drop target by matching the dragged site's position - * against all cells in the grid. - * @return The currently hovered drop target or null. - */ - _findDropTarget: function () { - // These are the minimum intersection values - we want to use the cell if - // the site is >= 50% hovering its position. - let minWidth = gDrag.cellWidth / 2; - let minHeight = gDrag.cellHeight / 2; - - let cellPositions = this._getCellPositions(); - let rect = gTransformation.getNodePosition(gDrag.draggedSite.node); - - // Compare each cell's position to the dragged site's position. - for (let i = 0; i < cellPositions.length; i++) { - let inter = rect.intersect(cellPositions[i].rect); - - // If the intersection is big enough we found a drop target. - if (inter.width >= minWidth && inter.height >= minHeight) - return cellPositions[i].cell; - } - - // No drop target found. - return null; - }, - - /** - * Gets the positions of all cell nodes. - * @return The (cached) cell positions. - */ - _getCellPositions: function DropTargetShim_getCellPositions() { - if (this._cellPositions) - return this._cellPositions; - - return this._cellPositions = gGrid.cells.map(function (cell) { - return {cell: cell, rect: gTransformation.getNodePosition(cell.node)}; - }); - }, - - /** - * Dispatches a custom DragEvent on the given target node. - * @param aEvent The source event. - * @param aType The event type. - * @param aTarget The target node that receives the event. - */ - _dispatchEvent: function (aEvent, aType, aTarget) { - let node = aTarget.node; - let event = document.createEvent("DragEvent"); - - // The event should not bubble to prevent recursion. - event.initDragEvent(aType, false, true, window, 0, 0, 0, 0, 0, false, false, - false, false, 0, node, aEvent.dataTransfer); - - node.dispatchEvent(event); - } -}; diff --git a/components/newtab/grid.js b/components/newtab/grid.js deleted file mode 100644 index e63ea54..0000000 --- a/components/newtab/grid.js +++ /dev/null @@ -1,175 +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 grid that contains all sites. - */ -var gGrid = { - /** - * The DOM node of the grid. - */ - _node: null, - _gridDefaultContent: null, - get node() { return this._node; }, - - /** - * The cached DOM fragment for sites. - */ - _siteFragment: null, - - /** - * All cells contained in the grid. - */ - _cells: [], - get cells() { return this._cells; }, - - /** - * All sites contained in the grid's cells. Sites may be empty. - */ - get sites() { - // return [for (cell of this.cells) cell.site]; - let aSites = []; - for (let cell of this.cells) { - aSites.push(cell.site); - } - return aSites; - }, - - // Tells whether the grid has already been initialized. - get ready() { return !!this._ready; }, - - // Returns whether the page has finished loading yet. - get isDocumentLoaded() { return document.readyState == "complete"; }, - - /** - * Initializes the grid. - * @param aSelector The query selector of the grid. - */ - init: function Grid_init() { - this._node = document.getElementById("newtab-grid"); - this._gridDefaultContent = this._node.lastChild; - this._createSiteFragment(); - - gLinks.populateCache(() => { - this._refreshGrid(); - this._ready = true; - }); - }, - - /** - * Creates a new site in the grid. - * @param aLink The new site's link. - * @param aCell The cell that will contain the new site. - * @return The newly created site. - */ - createSite: function Grid_createSite(aLink, aCell) { - let node = aCell.node; - node.appendChild(this._siteFragment.cloneNode(true)); - return new Site(node.firstElementChild, aLink); - }, - - /** - * Handles all grid events. - */ - handleEvent: function Grid_handleEvent(aEvent) { - // Any specific events should go here. - }, - - /** - * Locks the grid to block all pointer events. - */ - lock: function Grid_lock() { - this.node.setAttribute("locked", "true"); - }, - - /** - * Unlocks the grid to allow all pointer events. - */ - unlock: function Grid_unlock() { - this.node.removeAttribute("locked"); - }, - - /** - * Renders the grid. - */ - refresh() { - this._refreshGrid(); - }, - - /** - * Renders the grid, including cells and sites. - */ - _refreshGrid() { - let row = document.createElementNS(HTML_NAMESPACE, "div"); - row.classList.add("newtab-row"); - let cell = document.createElementNS(HTML_NAMESPACE, "div"); - cell.classList.add("newtab-cell"); - - // Clear the grid - this._node.innerHTML = ""; - - // Creates the structure of one row - for (let i = 0; i < gGridPrefs.gridColumns; i++) { - row.appendChild(cell.cloneNode(true)); - } - - // Creates the grid - for (let j = 0; j < gGridPrefs.gridRows; j++) { - this._node.appendChild(row.cloneNode(true)); - } - - // Create cell array. - let cellElements = this.node.querySelectorAll(".newtab-cell"); - let cells = Array.from(cellElements, (cell) => new Cell(this, cell)); - - // Fetch links. - let links = gLinks.getLinks(); - - // Create sites. - let numLinks = Math.min(links.length, cells.length); - for (let i = 0; i < numLinks; i++) { - if (links[i]) { - this.createSite(links[i], cells[i]); - } - } - - this._cells = cells; - }, - - /** - * Creates the DOM fragment that is re-used when creating sites. - */ - _createSiteFragment: function Grid_createSiteFragment() { - 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 = - '<a class="newtab-link">' + - ' <span class="newtab-thumbnail placeholder"/>' + - ' <span class="newtab-thumbnail 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); - }, - - /** - * Test a tile at a given position for being pinned or history - * @param position Position in sites array - */ - _isHistoricalTile: function Grid_isHistoricalTile(aPos) { - let site = this.sites[aPos]; - return site && (site.isPinned() || site.link && site.link.type == "history"); - } - -}; diff --git a/components/newtab/jar.mn b/components/newtab/jar.mn deleted file mode 100644 index 2d62914..0000000 --- a/components/newtab/jar.mn +++ /dev/null @@ -1,8 +0,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/. - -browser.jar: - content/browser/newtab/newTab.xhtml -* content/browser/newtab/newTab.js - content/browser/newtab/newTab.css
\ No newline at end of file diff --git a/components/newtab/moz.build b/components/newtab/moz.build deleted file mode 100644 index 2d64d50..0000000 --- a/components/newtab/moz.build +++ /dev/null @@ -1,8 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -JAR_MANIFESTS += ['jar.mn'] - diff --git a/components/newtab/newTab.css b/components/newtab/newTab.css deleted file mode 100644 index 3c7cfa1..0000000 --- a/components/newtab/newTab.css +++ /dev/null @@ -1,349 +0,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/. */ - -html { - width: 100%; - height: 100%; -} - -body { - font: message-box; - width: 100%; - height: 100%; - padding: 0; - margin: 0; - background-color: #F9F9F9; - display: -moz-box; - position: relative; - -moz-box-flex: 1; - -moz-user-focus: normal; - -moz-box-orient: vertical; -} - -input { - font: message-box; - font-size: 16px; -} - -input[type=button] { - cursor: pointer; -} - -/* UNDO */ -#newtab-undo-container { - transition: opacity 100ms ease-out; - -moz-box-align: center; - -moz-box-pack: center; -} - -#newtab-undo-container[undo-disabled] { - opacity: 0; - pointer-events: none; -} - -/* TOGGLE */ -#newtab-toggle { - position: absolute; - top: 12px; - right: 12px; -} - -#newtab-toggle:-moz-locale-dir(rtl) { - left: 12px; - right: auto; -} - -/* MARGINS */ -#newtab-vertical-margin { - display: -moz-box; - position: relative; - -moz-box-flex: 1; - -moz-box-orient: vertical; -} - -#newtab-margin-undo-container { - display: -moz-box; - left: 6px; - position: absolute; - top: 6px; - z-index: 1; -} - -#newtab-margin-undo-container:dir(rtl) { - left: auto; - right: 6px; -} - -#newtab-undo-close-button:dir(rtl) { - float:left; -} - -#newtab-horizontal-margin { - display: -moz-box; - -moz-box-flex: 5; -} - -#newtab-margin-top { - min-height: 10px; - max-height: 30px; - display: -moz-box; - -moz-box-flex: 1; - -moz-box-align: center; - -moz-box-pack: center; -} - -#newtab-margin-bottom { - min-height: 40px; - max-height: 80px; - -moz-box-flex: 1; -} - -.newtab-side-margin { - min-width: 40px; - max-width: 300px; - -moz-box-flex: 1; -} - -/* GRID */ -#newtab-grid { - display: -moz-box; - -moz-box-flex: 5; - -moz-box-orient: vertical; - min-width: 600px; - min-height: 400px; - transition: 175ms ease-out; - transition-property: opacity; -} - -#newtab-grid[page-disabled] { - opacity: 0; -} - -#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; -} - -/* - * Thumbnail image sizes are determined in the preferences: - * toolkit.pageThumbs.minWidth - * toolkit.pageThumbs.minHeight - */ -/* CELLS */ -.newtab-cell { - display: -moz-box; - -moz-box-flex: 1; -} - -/* SITES */ -.newtab-site { - position: relative; - -moz-box-flex: 1; - transition: 150ms ease-out; - transition-property: top, left, opacity; -} - -.newtab-site[frozen] { - position: absolute; - pointer-events: none; -} - -.newtab-site[dragged] { - transition-property: none; - z-index: 10; -} - -/* LINK + THUMBNAILS */ -.newtab-link, -.newtab-thumbnail { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; -} - -/* TITLES */ -.newtab-title { - overflow: hidden; - position: absolute; - right: 0; - text-align: center; -} - -.newtab-title { - bottom: 0; - white-space: nowrap; - text-overflow: ellipsis; - vertical-align: middle; -} - -.newtab-title { - left: 0; - padding: 0 4px; -} - -/* CONTROLS */ -.newtab-control { - position: absolute; - opacity: 0; - transition: opacity 100ms ease-out; -} - -.newtab-control:-moz-focusring, -.newtab-cell:not([ignorehover]) > .newtab-site:hover > .newtab-control { - opacity: 1; -} - -.newtab-control[dragged] { - opacity: 0 !important; -} - -@media (-moz-touch-enabled) { - .newtab-control { - opacity: 1; - } -} - -/* DRAG & DROP */ - -/* - * This is just a temporary drag element used for dataTransfer.setDragImage() - * 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. - */ -.newtab-drag { - width: 1px; - height: 1px; - background-color: #fff; - opacity: 0.01; -} - -/* SEARCH */ -#searchContainer { - display: -moz-box; - position: relative; - -moz-box-pack: center; - margin: 10px 0 15px; -} - -#searchContainer[page-disabled] { - opacity: 0; - pointer-events: none; -} - -#searchForm { - display: -moz-box; - position: relative; - height: 36px; - -moz-box-flex: 1; - max-width: 600px; /* 2 * (290 cell width + 10 cell margin) */ -} - -#searchEngineLogo { - border: 1px transparent; - padding: 2px 4px; - margin: 0; - width: 32px; - height: 32px; - position: absolute; -} - -#searchText { - -moz-box-flex: 1; - padding-top: 6px; - padding-bottom: 6px; - padding-inline-start: 42px; - padding-inline-end: 8px; - background: hsla(0,0%,100%,.9) padding-box; - border: 1px solid; - border-spacing: 0; - border-radius: 2px 0 0 2px; - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); - box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset, - 0 0 2px hsla(210,65%,9%,.1) inset, - 0 1px 0 hsla(0,0%,100%,.2); - color: inherit; - unicode-bidi: plaintext; -} - -#searchText:dir(rtl) { - border-radius: 0 2px 2px 0; -} - -#searchText[aria-expanded="true"] { - border-radius: 2px 0 0 0; -} - -#searchText[aria-expanded="true"]:dir(rtl) { - border-radius: 0 2px 0 0; -} - -#searchText[keepfocus], -#searchText:focus { - border-color: hsla(216,100%,60%,.6) hsla(216,76%,52%,.6) hsla(214,100%,40%,.6); -} - -#searchSubmit { - margin-inline-start: -1px; - padding: 0; - border: 1px solid; - background-color: #e0e0e0; - color: black; - border-color: hsla(220,54%,20%,.15) hsla(220,54%,20%,.17) hsla(220,54%,20%,.2); - border-radius: 0 2px 2px 0; - box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, - 0 1px 0 hsla(0,0%,100%,.2); - cursor: pointer; - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; - width: 50px; -} - -#searchSubmit:dir(rtl) { - border-radius: 2px 0 0 2px; -} - -#searchSubmit:hover { - background-color: hsl(220,54%,20%); - color: white; -} - -#searchText:focus + #searchSubmit, -#searchText + #searchSubmit:hover { - border-color: #5985fc #4573e7 #3264d5; -} - -#searchText:focus + #searchSubmit, -#searchText[keepfocus] + #searchSubmit { - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, - 0 0 0 1px hsla(0,0%,100%,.1) inset, - 0 1px 0 hsla(220,54%,20%,.03); -} - -#searchText + #searchSubmit:hover { - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, - 0 0 0 1px hsla(0,0%,100%,.1) inset, - 0 1px 0 hsla(220,54%,20%,.03), - 0 0 4px hsla(216,100%,20%,.2); -} - -#searchText + #searchSubmit:hover:active { - box-shadow: 0 1px 1px hsla(221,79%,6%,.1) inset, - 0 0 1px hsla(221,79%,6%,.2) inset; - transition-duration: 0ms; -} - -.contentSearchSuggestionTable { - font: message-box; - font-size: 16px; -} diff --git a/components/newtab/newTab.js b/components/newtab/newTab.js deleted file mode 100644 index 0022f21..0000000 --- a/components/newtab/newTab.js +++ /dev/null @@ -1,69 +0,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/. */ - -"use strict"; - -var Cu = Components.utils; -var Ci = Components.interfaces; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/PageThumbs.jsm"); -Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm"); -Cu.import("resource://gre/modules/NewTabUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Rect", - "resource://gre/modules/Geometry.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); - -var { - links: gLinks, - allPages: gAllPages, - linkChecker: gLinkChecker, - pinnedLinks: gPinnedLinks, - blockedLinks: gBlockedLinks, - gridPrefs: gGridPrefs -} = NewTabUtils; - -XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() { - return Services.strings. - createBundle("chrome://browser/locale/newTab.properties"); -}); - -function newTabString(name, args) { - let stringName = "newtab." + name; - if (!args) { - return gStringBundle.GetStringFromName(stringName); - } - return gStringBundle.formatStringFromName(stringName, args, args.length); -} - -function inPrivateBrowsingMode() { - return PrivateBrowsingUtils.isContentWindowPrivate(window); -} - -const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml"; -const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - -const TILES_EXPLAIN_LINK = "https://support.mozilla.org/kb/how-do-tiles-work-firefox"; -const TILES_INTRO_LINK = "https://www.mozilla.org/firefox/tiles/"; -const TILES_PRIVACY_LINK = "https://www.mozilla.org/privacy/"; - -#include transformations.js -#include page.js -#include grid.js -#include cells.js -#include sites.js -#include drag.js -#include dragDataHelper.js -#include drop.js -#include dropTargetShim.js -#include dropPreview.js -#include updater.js -#include undo.js -#include search.js - -// Everything is loaded. Initialize the New Tab Page. -gPage.init(); diff --git a/components/newtab/newTab.xhtml b/components/newtab/newTab.xhtml deleted file mode 100644 index de000e7..0000000 --- a/components/newtab/newTab.xhtml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- 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/. --> - -<!DOCTYPE html [ - <!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd"> - %newTabDTD; - <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> - %browserDTD; - <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; -]> - -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> - <title>&newtab.pageTitle;</title> - - <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/" /> - <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/newtab/newTab.css" /> - <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/newtab/newTab.css" /> -</head> - -<body dir="&locale.dir;"> - <div id="newtab-vertical-margin"> - <div id="newtab-margin-top"/> - - <div id="newtab-margin-undo-container"> - <div id="newtab-undo-container" undo-disabled="true"> - <label id="newtab-undo-label">&newtab.undo.removedLabel;</label> - <button id="newtab-undo-button" tabindex="-1" - class="newtab-undo-button">&newtab.undo.undoButton;</button> - <button id="newtab-undo-restore-button" tabindex="-1" - class="newtab-undo-button">&newtab.undo.restoreButton;</button> - <button id="newtab-undo-close-button" tabindex="-1" title="&newtab.undo.closeTooltip;"/> - </div> - </div> - - <div id="searchContainer"> - <form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)"> - <div id="searchLogoContainer"><img id="searchEngineLogo"/></div> - <input type="text" name="q" value="" id="searchText" maxlength="256"/> - <input id="searchSubmit" type="submit" value="&newtab.searchEngineButton.label;"/> - </form> - </div> - - <div id="newtab-horizontal-margin"> - <div class="newtab-side-margin"/> - <div id="newtab-grid"> - <!-- site grid --> - </div> - <div class="newtab-side-margin"/> - </div> - - <div id="newtab-margin-bottom"/> - <input id="newtab-toggle" type="button"/> - </div> -</body> -<script type="text/javascript;version=1.8" src="chrome://browser/content/newtab/newTab.js"/> -</html> diff --git a/components/newtab/page.js b/components/newtab/page.js deleted file mode 100644 index 34387fd..0000000 --- a/components/newtab/page.js +++ /dev/null @@ -1,244 +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 - -// The amount of time we wait while coalescing updates for hidden pages. -const SCHEDULE_UPDATE_TIMEOUT_MS = 1000; - -/** - * This singleton represents the whole 'New Tab Page' and takes care of - * initializing all its components. - */ -var gPage = { - /** - * Initializes the page. - */ - init: function Page_init() { - // Add ourselves to the list of pages to receive notifications. - gAllPages.register(this); - - // Listen for 'unload' to unregister this page. - addEventListener("unload", this, false); - - // Listen for toggle button clicks. - let button = document.getElementById("newtab-toggle"); - button.addEventListener("click", e => this.toggleEnabled(e)); - - // XXX bug 991111 - Not all click events are correctly triggered when - // listening from xhtml nodes -- in particular middle clicks on sites, so - // listen from the xul window and filter then delegate - addEventListener("click", this, false); - - // Check if the new tab feature is enabled. - let enabled = gAllPages.enabled; - if (enabled) - this._init(); - - this._updateAttributes(enabled); - }, - - /** - * Listens for notifications specific to this page. - */ - observe: function Page_observe(aSubject, aTopic, aData) { - if (aTopic == "nsPref:changed") { - let enabled = gAllPages.enabled; - this._updateAttributes(enabled); - - // Initialize the whole page if we haven't done that, yet. - if (enabled) { - this._init(); - } else { - gUndoDialog.hide(); - } - } else if (aTopic == "page-thumbnail:create" && gGrid.ready) { - for (let site of gGrid.sites) { - if (site && site.url === aData) { - site.refreshThumbnail(); - } - } - } - }, - - /** - * Updates the page's grid right away for visible pages. If the page is - * currently hidden, i.e. in a background tab or in the preloader, then we - * batch multiple update requests and refresh the grid once after a short - * delay. Accepts a single parameter the specifies the reason for requesting - * a page update. The page may decide to delay or prevent a requested updated - * based on the given reason. - */ - update(reason = "") { - // Update immediately if we're visible. - if (!document.hidden) { - // Ignore updates where reason=links-changed as those signal that the - // provider's set of links changed. We don't want to update visible pages - // in that case, it is ok to wait until the user opens the next tab. - if (reason != "links-changed" && gGrid.ready) { - gGrid.refresh(); - } - - return; - } - - // Bail out if we scheduled before. - if (this._scheduleUpdateTimeout) { - return; - } - - this._scheduleUpdateTimeout = setTimeout(() => { - // Refresh if the grid is ready. - if (gGrid.ready) { - gGrid.refresh(); - } - - this._scheduleUpdateTimeout = null; - }, SCHEDULE_UPDATE_TIMEOUT_MS); - }, - - /** - * Internally initializes the page. This runs only when/if the feature - * is/gets enabled. - */ - _init: function Page_init() { - if (this._initialized) - return; - - this._initialized = true; - - // Set submit button label for when CSS background are disabled (e.g. - // high contrast mode). - document.getElementById("searchSubmit").value = - document.body.getAttribute("dir") == "ltr" ? "\u25B6" : "\u25C0"; - - if (document.hidden) { - addEventListener("visibilitychange", this); - } else { - setTimeout(() => this.onPageFirstVisible()); - } - - // Initialize and render the grid. - 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, false); - document.addEventListener("drop", this, false); -#endif - }, - - /** - * Updates the 'page-disabled' attributes of the respective DOM nodes. - * @param aValue Whether the New Tab Page is enabled or not. - */ - _updateAttributes: function Page_updateAttributes(aValue) { - // Set the nodes' states. - let nodeSelector = "#newtab-grid, #searchContainer"; - for (let node of document.querySelectorAll(nodeSelector)) { - if (aValue) - node.removeAttribute("page-disabled"); - else - node.setAttribute("page-disabled", "true"); - } - - // Enables/disables the control and link elements. - let inputSelector = ".newtab-control, .newtab-link"; - for (let input of document.querySelectorAll(inputSelector)) { - if (aValue) - input.removeAttribute("tabindex"); - else - input.setAttribute("tabindex", "-1"); - } - }, - - /** - * Handles unload event - */ - _handleUnloadEvent: function Page_handleUnloadEvent() { - gAllPages.unregister(this); - }, - - /** - * Handles all page events. - */ - handleEvent: function Page_handleEvent(aEvent) { - switch (aEvent.type) { - case "load": - this.onPageVisibleAndLoaded(); - break; - case "unload": - this._handleUnloadEvent(); - break; - case "click": - let {button, target} = aEvent; - // Go up ancestors until we find a Site or not - while (target) { - if (target.hasOwnProperty("_newtabSite")) { - target._newtabSite.onClick(aEvent); - break; - } - target = target.parentNode; - } - 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; - case "visibilitychange": - // Cancel any delayed updates for hidden pages now that we're visible. - if (this._scheduleUpdateTimeout) { - clearTimeout(this._scheduleUpdateTimeout); - this._scheduleUpdateTimeout = null; - - // An update was pending so force an update now. - this.update(); - } - - setTimeout(() => this.onPageFirstVisible()); - removeEventListener("visibilitychange", this); - break; - } - }, - - onPageFirstVisible: function () { - // Record another page impression. - Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true); - - for (let site of gGrid.sites) { - if (site) { - // The site may need to modify and/or re-render itself if - // something changed after newtab was created by preloader. - // For example, the suggested tile endTime may have passed. - site.onFirstVisible(); - } - } - - // save timestamp to compute page life-span delta - this._firstVisibleTime = Date.now(); - - if (document.readyState == "complete") { - this.onPageVisibleAndLoaded(); - } else { - addEventListener("load", this); - } - }, - - onPageVisibleAndLoaded() { - }, - - toggleEnabled: function(aEvent) { - gAllPages.enabled = !gAllPages.enabled; - aEvent.stopPropagation(); - } -}; diff --git a/components/newtab/search.js b/components/newtab/search.js deleted file mode 100644 index 8bc959e..0000000 --- a/components/newtab/search.js +++ /dev/null @@ -1,134 +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 - -const SEARCH_ENGINES = { - "DuckDuckGo": { - image: "data:image/png;base64," + - "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAACT1BMVEXvISn/////9/fvUlr3ra3/" + - "zs7/7+/va2v/5+f/xsbvMTn/tbX/3t7/vb3vOUL3WmPvQkr/zgDvKTHvSlL3hIT3paX/1tbnISn3" + - "c3v3e3v3a3P3jIz3nJz/tb33c3PvKSn3lJT39/cAc73vSkr3e4Tv7+/3Yxj3pa3/tQj3jJT3nKX3" + - "Y2P/xs73hIzvQkL/vQjvQiHn5+f3hBD/ztbvMTH/vcb/3ucIc733lJz/pQilzufe7/fvMSHOzs73" + - "//cQrUpKvVprxmP3Y2vvShiUzmvWlJRzzmMYtUrvOTnn7/davVrWra3v9//nY2PvISGUxudztd7e" + - "3t7/76XvKSHea2v/xgDnOUK93vfW5/f/1t73Uhj/52ut3q2l3rXO784pjMZrrdb/rQjera3/5+/e" + - "paWMxufO79aEazkYrUr/nAj3jBD3axj3lBD///fehIRKpd7/1hCEYzk5vVL3//8ptVLW77UxtVLn" + - "SlLW1tZCvVp7vef/1gj/3invSkL//+fWtbXvpaX/3kr/97XvnJznWmMxjM5zvefOxsbWnKXWjIzG" + - "3u/ea3Pn997O5/fnQkqExuf3Whit1u/nUlrnxs7v5+d7zmuU1pT3exDOSjFjrVL/987/pUoQe8b/" + - "75T/3jFKxnO158bWKSl7zoRSxmtajEK1e0pzxlqcUjH/1iHOMSnOvb33cxDWnJx7td6EzmP/74xz" + - "azlrcznec3Pe771jxlpzczne78YpvVqEvWPn99YxvWOtSjHee3vG787OOTE5lEK1QjHv9+drzmve" + - "tbXO772q+r8wAAAFbUlEQVR4Xo2X84PzTBDHN3Zqu2fbemzbNl7atm3btvGHvTNJ2myuyd3NL2mT" + - "zmdnvjM76RImyGQlH5dCHBeSmscNmQkyfwBrZMLEY2aRF5cMSDYPEx+LZpUlAYRQbVEpnuc1je/M" + - "SbVwYoVFAbpE0IaLmiwqiVymmE3H84YuGs2mheCEhQH5qPUrje2ONxHKVIkXR2x2MxsMkDnLvftk" + - "2fSTQNCzSAgngwCCipkXxHiU+BsnCDFE8f6AQgnwaTGhkmDLymW8jPsBeIsth8iCpha618El1wgo" + - "4FOhWyWLWY+O8pbnAwTI29S1ElncJBmF4L0AGeJSdR4dUpt5w+DL0nAgoUuGGKKCBxDCOxrykaDb" + - "+yFQjhUylLlXpAB5jGnIqV6uvvWUcAAhLmDBXIAMrkXRdHQ+cerUiWefq1hRrAgg8LikUgdkQUAx" + - "6+2Ze0WLEO/1BQzrHCFNrAPAeDSD4q/Ln6R3p68MSYzDAUiwIEutJM0bHXE/gpEhJMxaAB3T6aT8" + - "mfkm+QBiMlwKFqAHvrHu9tvTOLrEdX4hFAkJWQB42qbVyam75ruv3zvF+wBCKJ0MAAV6SAy5+raA" + - "y+lb9tYBUw9sffKRJh+CDl2SAEAPquaC76swU1c+zlxbA9if/EIY78AcCBODDKjnVzDM0+sb57zq" + - "N14gdpbg4nraBaxm3NWpIDKNgJIIDTxEAKMyVM9/VrFcpijK52PbNhmk0RQORCA8dhGhIkDA+qPV" + - "Y/U8No2NHZsUfQCdzYTECSiRSRJKgxYAnK6+tnVrPYL7q2P7GNNnT0L3SQSS61AowK4BAExWq9XJ" + - "OmDT5D4GtUab7p92W1aD6AFBOjUKcONNKMG2o9vmScmhd+v5SCTS91StDLBwmHR5q0iiM4yv3X5g" + - "sD1i24tUHc0GQOrOihdw+ZV7drx+8I1IzfpaCQ1oSIGsbqEBdxy8KkLb8dYt7m7AFBpEJI8OUIAd" + - "Hve+wX509IqYgzLqxKMi5X+r6737wgHfMrZBKGwpQMWP0PN8/8qLn15cSRosEQeI3coxGrzRVfE2" + - "BEyTAMNpmbA3k2erPOyq+CUCPGvv3OmGykYBQhiYFbynDLu2uyW826qb7bSlv/VCe2R3vQqhIYQQ" + - "nLmSGKUAT1AqXn7V6p72iUsTThsNuhKUAeKMNFaiW2nG08H90IF1m6DywVdsHgA4bPgRGgAqUgBr" + - "DwxOtPcdv9RK6yklnaGKOXBMmN7RVCtJJMiUdG2s78dv9HbY7KrI9AQBOHwjaxaA6cKhRLXCHkpF" + - "PrAJYBz1su7LtSBQIjzozgI5AJDWsQ7gTJxETTHuEh5yW8kR5+1fvQBT5PDdWgPokE6GSuK3Aaby" + - "2KwNyGFIZ8/NfexVMAGXEfe8MA5QTVdrgGe2M9evev6FMwiAYr308nVzcx/SgHwSlswyLgDLHU0K" + - "tX5UZwCwZsM1b7516J1333v/g2UAuJoCNMsmZkEDZBXujCoOIfVJxQKsvXnDshvWfrEcAV9RAoqY" + - "rfdvHjY06R3tVmtjzQYsQ8ByC/C1O0dEzqkAGqELbiZ1W/RvBr51Ad9ZgO8dQCkh4/q5xvMC6hot" + - "sBl7rP1QT+HHQz9RGoSHhkyMgqEBdNPFWSWMY+1nBPxy+MjvZ2aZxB9n/zz3FwKiOTZfotb3AhhF" + - "xSUUNmGSjX+vWvPPYacVWJOkUilUT05ymEVb0JFHj9l/AVn+35b/jsx6YzNz8mja+iAEH7rYDntY" + - "Gaz3dizW080KWaeICx77kiG7lTKG6EEoPb0Wu0lZ9OA5whFH8GxHQjOMQls5HSs5t/glHX2FYtT/" + - "mGAs/fCtFU0vQJUSQYfvIBvVyukuLhbjuood/H6WCbD/AQSFvIO3JDxgAAAAAElFTkSuQmCC" - } -}; - -// This global tracks if the page has been set up before, to prevent double inits -var gInitialized = false; -var gObserver = new MutationObserver(function (mutations) { - for (let mutation of mutations) { - if (mutation.attributeName == "searchEngineURL") { - setupSearchEngine(); - if (!gInitialized) { - gInitialized = true; - } - return; - } - } -}); - -window.addEventListener("pageshow", function () { - window.gObserver.observe(document.documentElement, { attributes: true }); -}); - -window.addEventListener("pagehide", function() { - window.gObserver.disconnect(); -}); - -function onSearchSubmit(aEvent) { - let searchTerms = document.getElementById("searchText").value; - let searchURL = document.documentElement.getAttribute("searchEngineURL"); - - if (searchURL && searchTerms.length > 0) { - const SEARCH_TOKEN = "_searchTerms_"; - let searchPostData = document.documentElement.getAttribute("searchEnginePostData"); - if (searchPostData) { - // Check if a post form already exists. If so, remove it. - const POST_FORM_NAME = "searchFormPost"; - let form = document.forms[POST_FORM_NAME]; - if (form) { - form.parentNode.removeChild(form); - } - - // Create a new post form. - form = document.body.appendChild(document.createElement("form")); - form.setAttribute("name", POST_FORM_NAME); - // Set the URL to submit the form to. - form.setAttribute("action", searchURL.replace(SEARCH_TOKEN, searchTerms)); - form.setAttribute("method", "post"); - - // Create new <input type=hidden> elements for search param. - searchPostData = searchPostData.split("&"); - for (let postVar of searchPostData) { - let [name, value] = postVar.split("="); - if (value == SEARCH_TOKEN) { - value = searchTerms; - } - let input = document.createElement("input"); - input.setAttribute("type", "hidden"); - input.setAttribute("name", name); - input.setAttribute("value", value); - form.appendChild(input); - } - // Submit the form. - form.submit(); - } else { - searchURL = searchURL.replace(SEARCH_TOKEN, encodeURIComponent(searchTerms)); - window.location.href = searchURL; - } - } - - aEvent.preventDefault(); -} - - -function setupSearchEngine() { - let searchText = document.getElementById("searchText"); - let searchEngineName = document.documentElement.getAttribute("searchEngineName"); - let searchEngineInfo = SEARCH_ENGINES[searchEngineName]; - let logoElt = document.getElementById("searchEngineLogo"); - - // Add search engine logo. - if (searchEngineInfo && searchEngineInfo.image) { - logoElt.parentNode.hidden = false; - logoElt.src = searchEngineInfo.image; - logoElt.alt = searchEngineName; - searchText.placeholder = ""; - } else { - logoElt.parentNode.hidden = true; - searchText.placeholder = searchEngineName; - } -} diff --git a/components/newtab/sites.js b/components/newtab/sites.js deleted file mode 100644 index cb56752..0000000 --- a/components/newtab/sites.js +++ /dev/null @@ -1,353 +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 - -const THUMBNAIL_PLACEHOLDER_ENABLED = - Services.prefs.getBoolPref("browser.newtabpage.thumbnailPlaceholder"); - -/** - * This class represents a site that is contained in a cell and can be pinned, - * moved around or deleted. - */ -function Site(aNode, aLink) { - this._node = aNode; - this._node._newtabSite = this; - - this._link = aLink; - - this._render(); - this._addEventHandlers(); -} - -Site.prototype = { - /** - * The site's DOM node. - */ - get node() { return this._node; }, - - /** - * The site's link. - */ - get link() { return this._link; }, - - /** - * The url of the site's link. - */ - get url() { return this.link.url; }, - - /** - * The title of the site's link. - */ - get title() { return this.link.title || this.link.url; }, - - /** - * The site's parent cell. - */ - get cell() { - let parentNode = this.node.parentNode; - return parentNode && parentNode._newtabCell; - }, - - /** - * Pins the site on its current or a given index. - * @param aIndex The pinned index (optional). - * @return true if link changed type after pin - */ - pin: function Site_pin(aIndex) { - if (typeof aIndex == "undefined") - aIndex = this.cell.index; - - this._updateAttributes(true); - let changed = gPinnedLinks.pin(this._link, aIndex); - if (changed) { - // render site again - this._render(); - } - return changed; - }, - - /** - * Unpins the site and calls the given callback when done. - */ - unpin: function Site_unpin() { - if (this.isPinned()) { - this._updateAttributes(false); - gPinnedLinks.unpin(this._link); - gUpdater.updateGrid(); - } - }, - - /** - * Checks whether this site is pinned. - * @return Whether this site is pinned. - */ - isPinned: function Site_isPinned() { - return gPinnedLinks.isPinned(this._link); - }, - - /** - * Blocks the site (removes it from the grid) and calls the given callback - * when done. - */ - block: function Site_block() { - if (!gBlockedLinks.isBlocked(this._link)) { - gUndoDialog.show(this); - gBlockedLinks.block(this._link); - gUpdater.updateGrid(); - } - }, - - /** - * Gets the DOM node specified by the given query selector. - * @param aSelector The query selector. - * @return The DOM node we found. - */ - _querySelector: function Site_querySelector(aSelector) { - return this.node.querySelector(aSelector); - }, - - /** - * Updates attributes for all nodes which status depends on this site being - * pinned or unpinned. - * @param aPinned Whether this site is now pinned or unpinned. - */ - _updateAttributes: function (aPinned) { - let control = this._querySelector(".newtab-control-pin"); - - if (aPinned) { - this.node.setAttribute("pinned", true); - control.setAttribute("title", newTabString("unpin")); - } else { - this.node.removeAttribute("pinned"); - control.setAttribute("title", newTabString("pin")); - } - }, - - _newTabString: function(str, substrArr) { - let regExp = /%[0-9]\$S/g; - let matches; - while ((matches = regExp.exec(str))) { - let match = matches[0]; - let index = match.charAt(1); // Get the digit in the regExp. - str = str.replace(match, substrArr[index - 1]); - } - return str; - }, - - /** - * Checks for and modifies link at campaign end time - */ - _checkLinkEndTime: function Site_checkLinkEndTime() { - if (this.link.endTime && this.link.endTime < Date.now()) { - let oldUrl = this.url; - // chop off the path part from url - this.link.url = Services.io.newURI(this.url, null, null).resolve("/"); - // clear supplied images - this triggers thumbnail download for new url - delete this.link.imageURI; - // remove endTime to avoid further time checks - delete this.link.endTime; - gPinnedLinks.replace(oldUrl, this.link); - } - }, - - /** - * Renders the site's data (fills the HTML fragment). - */ - _render: function Site_render() { - // first check for end time, as it may modify the link - this._checkLinkEndTime(); - // setup display variables - let url = this.url; - let title = this.link.type == "history" ? this.link.baseDomain : - this.title; - let tooltip = (this.title == url ? this.title : this.title + "\n" + url); - - let link = this._querySelector(".newtab-link"); - link.setAttribute("title", tooltip); - link.setAttribute("href", url); - this.node.setAttribute("type", this.link.type); - - let titleNode = this._querySelector(".newtab-title"); - titleNode.textContent = title; - if (this.link.titleBgColor) { - titleNode.style.backgroundColor = this.link.titleBgColor; - } - - if (this.isPinned()) - this._updateAttributes(true); - // Capture the page if the thumbnail is missing, which will cause page.js - // to be notified and call our refreshThumbnail() method. - this.captureIfMissing(); - // but still display whatever thumbnail might be available now. - this.refreshThumbnail(); - }, - - /** - * Called when the site's tab becomes visible for the first time. - * Since the newtab may be preloaded long before it's displayed, - * check for changed conditions and re-render if needed - */ - onFirstVisible: function Site_onFirstVisible() { - if (this.link.endTime && this.link.endTime < Date.now()) { - // site needs to change landing url and background image - this._render(); - } - else { - this.captureIfMissing(); - } - }, - - /** - * Captures the site's thumbnail in the background, but only if there's no - * existing thumbnail and the page allows background captures. - */ - captureIfMissing: function Site_captureIfMissing() { - if (!document.hidden && !this.link.imageURI) { - BackgroundPageThumbs.captureIfMissing(this.url); - } - }, - - /** - * Refreshes the thumbnail for the site. - */ - refreshThumbnail: function Site_refreshThumbnail() { - let link = this.link; - - let thumbnail = this._querySelector(".newtab-thumbnail.thumbnail"); - if (link.bgColor) { - thumbnail.style.backgroundColor = link.bgColor; - } - let uri = link.imageURI || PageThumbs.getThumbnailURL(this.url); - thumbnail.style.backgroundImage = 'url("' + uri + '")'; - - if (THUMBNAIL_PLACEHOLDER_ENABLED && - link.type == "history" && - link.baseDomain) { - let placeholder = this._querySelector(".newtab-thumbnail.placeholder"); - let charCodeSum = 0; - for (let c of link.baseDomain) { - charCodeSum += c.charCodeAt(0); - } - const COLORS = 16; - let hue = Math.round((charCodeSum % COLORS) / COLORS * 360); - placeholder.style.backgroundColor = "hsl(" + hue + ",80%,40%)"; - placeholder.textContent = link.baseDomain.substr(0,1).toUpperCase(); - } - }, - - _ignoreHoverEvents: function(element) { - element.addEventListener("mouseover", () => { - this.cell.node.setAttribute("ignorehover", "true"); - }); - element.addEventListener("mouseout", () => { - this.cell.node.removeAttribute("ignorehover"); - }); - }, - - /** - * Adds event handlers for the site and its buttons. - */ - _addEventHandlers: function Site_addEventHandlers() { - // Register drag-and-drop event handlers. - this._node.addEventListener("dragstart", this, false); - this._node.addEventListener("dragend", this, false); - this._node.addEventListener("mouseover", this, false); - }, - - /** - * Speculatively opens a connection to the current site. - */ - _speculativeConnect: function Site_speculativeConnect() { - let sc = Services.io.QueryInterface(Ci.nsISpeculativeConnect); - let uri = Services.io.newURI(this.url, null, null); - try { - // This can throw for certain internal URLs, when they wind up in - // about:newtab. Be sure not to propagate the error. - sc.speculativeConnect(uri, null); - } catch (e) {} - }, - - /** - * Record interaction with site using telemetry. - */ - _recordSiteClicked: function Site_recordSiteClicked(aIndex) { - if (Services.prefs.prefHasUserValue("browser.newtabpage.rows") || - Services.prefs.prefHasUserValue("browser.newtabpage.columns") || - aIndex > 8) { - // We only want to get indices for the default configuration, everything - // else goes in the same bucket. - aIndex = 9; - } - Services.telemetry.getHistogramById("NEWTAB_PAGE_SITE_CLICKED") - .add(aIndex); - }, - - _toggleLegalText: function(buttonClass, explanationTextClass) { - let button = this._querySelector(buttonClass); - if (button.hasAttribute("active")) { - let explain = this._querySelector(explanationTextClass); - explain.parentNode.removeChild(explain); - - button.removeAttribute("active"); - } - }, - - /** - * Handles site click events. - */ - onClick: function Site_onClick(aEvent) { - let action; - let pinned = this.isPinned(); - let tileIndex = this.cell.index; - let {button, target} = aEvent; - - // Handle tile/thumbnail link click - if (target.classList.contains("newtab-link") || - target.parentElement.classList.contains("newtab-link")) { - // Record for primary and middle clicks - if (button == 0 || button == 1) { - this._recordSiteClicked(tileIndex); - action = "click"; - } - } - // Only handle primary clicks for the remaining targets - else if (button == 0) { - aEvent.preventDefault(); - if (target.classList.contains("newtab-control-block")) { - this.block(); - action = "block"; - } - else if (pinned && target.classList.contains("newtab-control-pin")) { - this.unpin(); - action = "unpin"; - } - else if (!pinned && target.classList.contains("newtab-control-pin")) { - if (this.pin()) { - // link has changed - update rest of the pages - gAllPages.update(gPage); - } - action = "pin"; - } - } - }, - - /** - * Handles all site events. - */ - handleEvent: function Site_handleEvent(aEvent) { - switch (aEvent.type) { - case "mouseover": - this._node.removeEventListener("mouseover", this, false); - this._speculativeConnect(); - break; - case "dragstart": - gDrag.start(this, aEvent); - break; - case "dragend": - gDrag.end(this, aEvent); - break; - } - } -}; diff --git a/components/newtab/transformations.js b/components/newtab/transformations.js deleted file mode 100644 index f7db0ad..0000000 --- a/components/newtab/transformations.js +++ /dev/null @@ -1,270 +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 allows to transform the grid by repositioning a site's node - * in the DOM and by showing or hiding the node. It additionally provides - * convenience methods to work with a site's DOM node. - */ -var 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. - * @return A Rect instance with the position. - */ - getNodePosition: function Transformation_getNodePosition(aNode) { - let {left, top, width, height} = aNode.getBoundingClientRect(); - return new Rect(left + scrollX, top + scrollY, width, height); - }, - - /** - * Fades a given node from zero to full opacity. - * @param aNode The node to fade. - * @param aCallback The callback to call when finished. - */ - fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) { - this._setNodeOpacity(aNode, 1, function () { - // Clear the style property. - aNode.style.opacity = ""; - - if (aCallback) - aCallback(); - }); - }, - - /** - * Fades a given node from full to zero opacity. - * @param aNode The node to fade. - * @param aCallback The callback to call when finished. - */ - fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) { - this._setNodeOpacity(aNode, 0, aCallback); - }, - - /** - * Fades a given site from zero to full opacity. - * @param aSite The site to fade. - * @param aCallback The callback to call when finished. - */ - showSite: function Transformation_showSite(aSite, aCallback) { - this.fadeNodeIn(aSite.node, aCallback); - }, - - /** - * Fades a given site from full to zero opacity. - * @param aSite The site to fade. - * @param aCallback The callback to call when finished. - */ - hideSite: function Transformation_hideSite(aSite, aCallback) { - this.fadeNodeOut(aSite.node, aCallback); - }, - - /** - * Allows to set a site's position. - * @param aSite The site to re-position. - * @param aPosition The desired position for the given site. - */ - setSitePosition: function Transformation_setSitePosition(aSite, aPosition) { - let style = aSite.node.style; - let {top, left} = aPosition; - - style.top = top + "px"; - style.left = left + "px"; - }, - - /** - * Freezes a site in its current position by positioning it absolute. - * @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)); - }, - - /** - * Unfreezes a site by removing its absolute positioning. - * @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.width = style.height = ""; - aSite.node.removeAttribute("frozen"); - }, - - /** - * Slides the given site to the target node's position. - * @param aSite The site to move. - * @param aTarget The slide target. - * @param aOptions Set of options (see below). - * unfreeze - unfreeze the site after sliding - * callback - the callback to call when finished - */ - slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) { - let currentPosition = this.getNodePosition(aSite.node); - let targetPosition = this.getNodePosition(aTarget.node) - let callback = aOptions && aOptions.callback; - - let self = this; - - function finish() { - if (aOptions && aOptions.unfreeze) - self.unfreezeSitePosition(aSite); - - if (callback) - 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.left == targetPosition.left && - currentPosition.top == targetPosition.top) { - finish(); - } else { - this.setSitePosition(aSite, targetPosition); - this._whenTransitionEnded(aSite.node, ["left", "top"], finish); - } - }, - - /** - * Rearranges a given array of sites and moves them to their new positions or - * fades in/out new/removed sites. - * @param aSites An array of sites to rearrange. - * @param aOptions Set of options (see below). - * unfreeze - unfreeze the site after rearranging - * callback - the callback to call when finished - */ - rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) { - let batch = []; - let cells = gGrid.cells; - let callback = aOptions && aOptions.callback; - let unfreeze = aOptions && aOptions.unfreeze; - - aSites.forEach(function (aSite, aIndex) { - // Do not re-arrange empty cells or the dragged site. - if (!aSite || aSite == gDrag.draggedSite) - return; - - batch.push(new Promise(resolve => { - if (!cells[aIndex]) { - // The site disappeared from the grid, hide it. - this.hideSite(aSite, resolve); - } else if (this._getNodeOpacity(aSite.node) != 1) { - // The site disappeared before but is now back, show it. - this.showSite(aSite, resolve); - } else { - // The site's position has changed, move it around. - this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: resolve}); - } - })); - }, this); - - if (callback) { - Promise.all(batch).then(callback); - } - }, - - /** - * Listens for the 'transitionend' event on a given node and calls the given - * callback. - * @param aNode The node that is transitioned. - * @param aProperties The properties we'll wait to be transitioned. - * @param aCallback The callback to call when finished. - */ - _whenTransitionEnded: - function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) { - - let props = new Set(aProperties); - aNode.addEventListener("transitionend", function onEnd(e) { - if (props.has(e.propertyName)) { - aNode.removeEventListener("transitionend", onEnd); - aCallback(); - } - }); - }, - - /** - * Gets a given node's opacity value. - * @param aNode The node to get the opacity value from. - * @return The node's opacity value. - */ - _getNodeOpacity: function Transformation_getNodeOpacity(aNode) { - let cstyle = window.getComputedStyle(aNode, null); - return cstyle.getPropertyValue("opacity"); - }, - - /** - * Sets a given node's opacity. - * @param aNode The node to set the opacity value for. - * @param aOpacity The opacity value to set. - * @param aCallback The callback to call when finished. - */ - _setNodeOpacity: - function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) { - - if (this._getNodeOpacity(aNode) == aOpacity) { - if (aCallback) - aCallback(); - } else { - if (aCallback) { - this._whenTransitionEnded(aNode, ["opacity"], aCallback); - } - - aNode.style.opacity = aOpacity; - } - }, - - /** - * Moves a site to the cell with the given index. - * @param aSite The site to move. - * @param aIndex The target cell's index. - * @param aOptions Options that are directly passed to slideSiteTo(). - */ - _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"); - } -}; diff --git a/components/newtab/undo.js b/components/newtab/undo.js deleted file mode 100644 index b856914..0000000 --- a/components/newtab/undo.js +++ /dev/null @@ -1,116 +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 - -/** - * Dialog allowing to undo the removal of single site or to completely restore - * the grid's original state. - */ -var gUndoDialog = { - /** - * The undo dialog's timeout in miliseconds. - */ - HIDE_TIMEOUT_MS: 15000, - - /** - * Contains undo information. - */ - _undoData: null, - - /** - * Initializes the undo dialog. - */ - init: function UndoDialog_init() { - this._undoContainer = document.getElementById("newtab-undo-container"); - this._undoContainer.addEventListener("click", this, false); - this._undoButton = document.getElementById("newtab-undo-button"); - this._undoCloseButton = document.getElementById("newtab-undo-close-button"); - this._undoRestoreButton = document.getElementById("newtab-undo-restore-button"); - }, - - /** - * Shows the undo dialog. - * @param aSite The site that just got removed. - */ - show: function UndoDialog_show(aSite) { - if (this._undoData) - clearTimeout(this._undoData.timeout); - - this._undoData = { - index: aSite.cell.index, - wasPinned: aSite.isPinned(), - blockedLink: aSite.link, - timeout: setTimeout(this.hide.bind(this), this.HIDE_TIMEOUT_MS) - }; - - this._undoContainer.removeAttribute("undo-disabled"); - this._undoButton.removeAttribute("tabindex"); - this._undoCloseButton.removeAttribute("tabindex"); - this._undoRestoreButton.removeAttribute("tabindex"); - }, - - /** - * Hides the undo dialog. - */ - hide: function UndoDialog_hide() { - if (!this._undoData) - return; - - clearTimeout(this._undoData.timeout); - this._undoData = null; - this._undoContainer.setAttribute("undo-disabled", "true"); - this._undoButton.setAttribute("tabindex", "-1"); - this._undoCloseButton.setAttribute("tabindex", "-1"); - this._undoRestoreButton.setAttribute("tabindex", "-1"); - }, - - /** - * The undo dialog event handler. - * @param aEvent The event to handle. - */ - handleEvent: function UndoDialog_handleEvent(aEvent) { - switch (aEvent.target.id) { - case "newtab-undo-button": - this._undo(); - break; - case "newtab-undo-restore-button": - this._undoAll(); - break; - case "newtab-undo-close-button": - this.hide(); - break; - } - }, - - /** - * Undo the last blocked site. - */ - _undo: function UndoDialog_undo() { - if (!this._undoData) - return; - - let {index, wasPinned, blockedLink} = this._undoData; - gBlockedLinks.unblock(blockedLink); - - if (wasPinned) { - gPinnedLinks.pin(blockedLink, index); - } - - gUpdater.updateGrid(); - this.hide(); - }, - - /** - * Undo all blocked sites. - */ - _undoAll: function UndoDialog_undoAll() { - NewTabUtils.undoAll(function() { - gUpdater.updateGrid(); - this.hide(); - }.bind(this)); - } -}; - -gUndoDialog.init(); diff --git a/components/newtab/updater.js b/components/newtab/updater.js deleted file mode 100644 index 2bab74d..0000000 --- a/components/newtab/updater.js +++ /dev/null @@ -1,177 +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 provides functionality to update the current grid to a new - * set of pinned and blocked sites. It adds, moves and removes sites. - */ -var gUpdater = { - /** - * Updates the current grid according to its pinned and blocked sites. - * This removes old, moves existing and creates new sites to fill gaps. - * @param aCallback The callback to call when finished. - */ - updateGrid: function Updater_updateGrid(aCallback) { - let links = gLinks.getLinks().slice(0, gGrid.cells.length); - - // Find all sites that remain in the grid. - let sites = this._findRemainingSites(links); - - // Remove sites that are no longer in the grid. - this._removeLegacySites(sites, () => { - // Freeze all site positions so that we can move their DOM nodes around - // without any visual impact. - this._freezeSitePositions(sites); - - // Move the sites' DOM nodes to their new position in the DOM. This will - // have no visual effect as all the sites have been frozen and will - // remain in their current position. - this._moveSiteNodes(sites); - - // Now it's time to animate the sites actually moving to their new - // positions. - this._rearrangeSites(sites, () => { - // Try to fill empty cells and finish. - this._fillEmptyCells(links, aCallback); - - // Update other pages that might be open to keep them synced. - gAllPages.update(gPage); - }); - }); - }, - - /** - * Takes an array of links and tries to correlate them to sites contained in - * the current grid. If no corresponding site can be found (i.e. the link is - * new and a site will be created) then just set it to null. - * @param aLinks The array of links to find sites for. - * @return Array of sites mapped to the given links (can contain null values). - */ - _findRemainingSites: function Updater_findRemainingSites(aLinks) { - let map = {}; - - // Create a map to easily retrieve the site for a given URL. - gGrid.sites.forEach(function (aSite) { - if (aSite) - map[aSite.url] = aSite; - }); - - // Map each link to its corresponding site, if any. - return aLinks.map(function (aLink) { - return aLink && (aLink.url in map) && map[aLink.url]; - }); - }, - - /** - * Freezes the given sites' positions. - * @param aSites The array of sites to freeze. - */ - _freezeSitePositions: function Updater_freezeSitePositions(aSites) { - aSites.forEach(function (aSite) { - if (aSite) - gTransformation.freezeSitePosition(aSite); - }); - }, - - /** - * Moves the given sites' DOM nodes to their new positions. - * @param aSites The array of sites to move. - */ - _moveSiteNodes: function Updater_moveSiteNodes(aSites) { - let cells = gGrid.cells; - - // Truncate the given array of sites to not have more sites than cells. - // This can happen when the user drags a bookmark (or any other new kind - // of link) onto the grid. - let sites = aSites.slice(0, cells.length); - - sites.forEach(function (aSite, aIndex) { - let cell = cells[aIndex]; - let cellSite = cell.site; - - // The site's position didn't change. - if (!aSite || cellSite != aSite) { - let cellNode = cell.node; - - // Empty the cell if necessary. - if (cellSite) - cellNode.removeChild(cellSite.node); - - // Put the new site in place, if any. - if (aSite) - cellNode.appendChild(aSite.node); - } - }, this); - }, - - /** - * Rearranges the given sites and slides them to their new positions. - * @param aSites The array of sites to re-arrange. - * @param aCallback The callback to call when finished. - */ - _rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) { - let options = {callback: aCallback, unfreeze: true}; - gTransformation.rearrangeSites(aSites, options); - }, - - /** - * Removes all sites from the grid that are not in the given links array or - * exceed the grid. - * @param aSites The array of sites remaining in the grid. - * @param aCallback The callback to call when finished. - */ - _removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) { - let batch = []; - - // Delete sites that were removed from the grid. - gGrid.sites.forEach(function (aSite) { - // The site must be valid and not in the current grid. - if (!aSite || aSites.indexOf(aSite) != -1) - return; - - batch.push(new Promise(resolve => { - // Fade out the to-be-removed site. - gTransformation.hideSite(aSite, function () { - let node = aSite.node; - - // Remove the site from the DOM. - node.parentNode.removeChild(node); - resolve(); - }); - })); - }); - - Promise.all(batch).then(aCallback); - }, - - /** - * Tries to fill empty cells with new links if available. - * @param aLinks The array of links. - * @param aCallback The callback to call when finished. - */ - _fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) { - let {cells, sites} = gGrid; - - // Find empty cells and fill them. - Promise.all(sites.map((aSite, aIndex) => { - if (aSite || !aLinks[aIndex]) - return null; - - return new Promise(resolve => { - // Create the new site and fade it in. - let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]); - - // Set the site's initial opacity to zero. - site.node.style.opacity = 0; - - // Flush all style changes for the dynamically inserted site to make - // the fade-in transition work. - window.getComputedStyle(site.node).opacity; - gTransformation.showSite(site, resolve); - }); - })).then(aCallback).catch(console.exception); - } -}; |