browser/metro/base/content/startui/HistoryView.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/metro/base/content/startui/HistoryView.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,323 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +'use strict';
     1.9 +
    1.10 +function HistoryView(aSet, aFilterUnpinned) {
    1.11 +  View.call(this, aSet);
    1.12 +
    1.13 +  this._inBatch = 0;
    1.14 +
    1.15 +  // View monitors this for maximum tile display counts
    1.16 +  this.tilePrefName = "browser.display.startUI.history.maxresults";
    1.17 +  this.showing = this.maxTiles > 0;
    1.18 +
    1.19 +  this._filterUnpinned = aFilterUnpinned;
    1.20 +  this._historyService = PlacesUtils.history;
    1.21 +  this._navHistoryService = gHistSvc;
    1.22 +
    1.23 +  this._pinHelper = new ItemPinHelper("metro.history.unpinned");
    1.24 +  this._historyService.addObserver(this, false);
    1.25 +  StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
    1.26 +  StartUI.chromeWin.addEventListener('HistoryNeedsRefresh', this, false);
    1.27 +  window.addEventListener("TabClose", this, true);
    1.28 +}
    1.29 +
    1.30 +HistoryView.prototype = Util.extend(Object.create(View.prototype), {
    1.31 +  _set: null,
    1.32 +  _toRemove: null,
    1.33 +
    1.34 +  // For View's showing property
    1.35 +  get vbox() {
    1.36 +    return document.getElementById("start-history");
    1.37 +  },
    1.38 +
    1.39 +  destruct: function destruct() {
    1.40 +    this._historyService.removeObserver(this);
    1.41 +    if (StartUI.chromeWin) {
    1.42 +      StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
    1.43 +      StartUI.chromeWin.removeEventListener('HistoryNeedsRefresh', this, false);
    1.44 +    }
    1.45 +    View.prototype.destruct.call(this);
    1.46 +  },
    1.47 +
    1.48 +  refreshView: function () {
    1.49 +    this.onClearHistory();
    1.50 +    this.populateGrid();
    1.51 +  },
    1.52 +
    1.53 +  handleItemClick: function tabview_handleItemClick(aItem) {
    1.54 +    let url = aItem.getAttribute("value");
    1.55 +    StartUI.goToURI(url);
    1.56 +  },
    1.57 +
    1.58 +  populateGrid: function populateGrid(aRefresh) {
    1.59 +    this._inBatch++; // always batch up grid updates to avoid redundant arrangeItems calls
    1.60 +    let query = this._navHistoryService.getNewQuery();
    1.61 +    let options = this._navHistoryService.getNewQueryOptions();
    1.62 +    options.excludeQueries = true;
    1.63 +    options.queryType = options.QUERY_TYPE_HISTORY;
    1.64 +    options.resultType = options.RESULTS_AS_URI;
    1.65 +    options.sortingMode = options.SORT_BY_DATE_DESCENDING;
    1.66 +
    1.67 +    let limit = this.maxTiles;
    1.68 +    let result = this._navHistoryService.executeQuery(query, options);
    1.69 +    let rootNode = result.root;
    1.70 +    rootNode.containerOpen = true;
    1.71 +    let childCount = rootNode.childCount;
    1.72 +
    1.73 +    for (let i = 0, addedCount = 0; i < childCount && addedCount < limit; i++) {
    1.74 +      let node = rootNode.getChild(i);
    1.75 +      let uri = node.uri;
    1.76 +      let title = (node.title && node.title.length) ? node.title : uri;
    1.77 +
    1.78 +      // If item is marked for deletion, skip it.
    1.79 +      if (this._toRemove && this._toRemove.indexOf(uri) !== -1)
    1.80 +        continue;
    1.81 +
    1.82 +      let items = this._set.getItemsByUrl(uri);
    1.83 +
    1.84 +      // Item has been unpinned, skip if filterUnpinned set.
    1.85 +      if (this._filterUnpinned && !this._pinHelper.isPinned(uri)) {
    1.86 +        if (items.length > 0)
    1.87 +          this.removeHistory(uri);
    1.88 +
    1.89 +        continue;
    1.90 +      }
    1.91 +
    1.92 +      if (!aRefresh || items.length === 0) {
    1.93 +        // If we're not refreshing or the item is not in the grid, add it.
    1.94 +        this.addItemToSet(uri, title, node.icon, addedCount);
    1.95 +      } else if (aRefresh && items.length > 0) {
    1.96 +        // Update context action in case it changed in another view.
    1.97 +        for (let item of items) {
    1.98 +          this._setContextActions(item);
    1.99 +        }
   1.100 +      }
   1.101 +
   1.102 +      addedCount++;
   1.103 +    }
   1.104 +
   1.105 +    // Remove extra items in case a refresh added more than the limit.
   1.106 +    // This can happen when undoing a delete.
   1.107 +    if (aRefresh) {
   1.108 +      while (this._set.itemCount > limit)
   1.109 +        this._set.removeItemAt(this._set.itemCount - 1);
   1.110 +    }
   1.111 +
   1.112 +    rootNode.containerOpen = false;
   1.113 +    this._set.arrangeItems();
   1.114 +    if (this._inBatch > 0)
   1.115 +      this._inBatch--;
   1.116 +  },
   1.117 +
   1.118 +  addItemToSet: function addItemToSet(aURI, aTitle, aIcon, aPos) {
   1.119 +    let item = this._set.insertItemAt(aPos || 0, aTitle, aURI, this._inBatch);
   1.120 +    this._setContextActions(item);
   1.121 +    this._updateFavicon(item, aURI);
   1.122 +  },
   1.123 +
   1.124 +  _setContextActions: function bv__setContextActions(aItem) {
   1.125 +    let uri = aItem.getAttribute("value");
   1.126 +    aItem.setAttribute("data-contextactions", "delete," + (this._pinHelper.isPinned(uri) ? "hide" : "pin"));
   1.127 +    if ("refresh" in aItem) aItem.refresh();
   1.128 +  },
   1.129 +
   1.130 +  _sendNeedsRefresh: function bv__sendNeedsRefresh(){
   1.131 +    // Event sent when all views need to refresh.
   1.132 +    let event = document.createEvent("Events");
   1.133 +    event.initEvent("HistoryNeedsRefresh", true, false);
   1.134 +    window.dispatchEvent(event);
   1.135 +  },
   1.136 +
   1.137 +  removeHistory: function (aUri) {
   1.138 +    let items = this._set.getItemsByUrl(aUri);
   1.139 +    for (let item of items)
   1.140 +      this._set.removeItem(item, true);
   1.141 +    if (!this._inBatch)
   1.142 +      this._set.arrangeItems();
   1.143 +  },
   1.144 +
   1.145 +  doActionOnSelectedTiles: function bv_doActionOnSelectedTiles(aActionName, aEvent) {
   1.146 +    let tileGroup = this._set;
   1.147 +    let selectedTiles = tileGroup.selectedItems;
   1.148 +
   1.149 +    // just arrange the grid once at the end of any action handling
   1.150 +    this._inBatch = true;
   1.151 +
   1.152 +    switch (aActionName){
   1.153 +      case "delete":
   1.154 +        Array.forEach(selectedTiles, function(aNode) {
   1.155 +          if (!this._toRemove) {
   1.156 +            this._toRemove = [];
   1.157 +          }
   1.158 +
   1.159 +          let uri = aNode.getAttribute("value");
   1.160 +
   1.161 +          this._toRemove.push(uri);
   1.162 +          this.removeHistory(uri);
   1.163 +        }, this);
   1.164 +
   1.165 +        // stop the appbar from dismissing
   1.166 +        aEvent.preventDefault();
   1.167 +
   1.168 +        // at next tick, re-populate the context appbar.
   1.169 +        setTimeout(function(){
   1.170 +          // fire a MozContextActionsChange event to update the context appbar
   1.171 +          let event = document.createEvent("Events");
   1.172 +          // we need the restore button to show (the tile node will go away though)
   1.173 +          event.actions = ["restore"];
   1.174 +          event.initEvent("MozContextActionsChange", true, false);
   1.175 +          tileGroup.dispatchEvent(event);
   1.176 +        }, 0);
   1.177 +        break;
   1.178 +
   1.179 +      case "restore":
   1.180 +        // clear toRemove and let _sendNeedsRefresh update the items.
   1.181 +        this._toRemove = null;
   1.182 +        break;
   1.183 +
   1.184 +      case "unpin":
   1.185 +        Array.forEach(selectedTiles, function(aNode) {
   1.186 +          let uri = aNode.getAttribute("value");
   1.187 +
   1.188 +          if (this._filterUnpinned)
   1.189 +            this.removeHistory(uri);
   1.190 +
   1.191 +          this._pinHelper.setUnpinned(uri);
   1.192 +        }, this);
   1.193 +        break;
   1.194 +
   1.195 +      case "pin":
   1.196 +        Array.forEach(selectedTiles, function(aNode) {
   1.197 +          let uri = aNode.getAttribute("value");
   1.198 +
   1.199 +          this._pinHelper.setPinned(uri);
   1.200 +        }, this);
   1.201 +        break;
   1.202 +
   1.203 +      default:
   1.204 +        this._inBatch = false;
   1.205 +        return;
   1.206 +    }
   1.207 +
   1.208 +    this._inBatch = false;
   1.209 +    // Send refresh event so all view are in sync.
   1.210 +    this._sendNeedsRefresh();
   1.211 +  },
   1.212 +
   1.213 +  handleEvent: function bv_handleEvent(aEvent) {
   1.214 +    switch (aEvent.type){
   1.215 +      case "MozAppbarDismissing":
   1.216 +        // If undo wasn't pressed, time to do definitive actions.
   1.217 +        if (this._toRemove) {
   1.218 +          for (let uri of this._toRemove) {
   1.219 +            this._historyService.removePage(NetUtil.newURI(uri));
   1.220 +          }
   1.221 +
   1.222 +          // Clear context app bar
   1.223 +          let event = document.createEvent("Events");
   1.224 +          event.actions = [];
   1.225 +          event.initEvent("MozContextActionsChange", true, false);
   1.226 +          this._set.dispatchEvent(event);
   1.227 +
   1.228 +          this._toRemove = null;
   1.229 +        }
   1.230 +        break;
   1.231 +
   1.232 +      case "HistoryNeedsRefresh":
   1.233 +        this.populateGrid(true);
   1.234 +        break;
   1.235 +
   1.236 +      case "TabClose":
   1.237 +        // Flush any pending actions - appbar will call us back
   1.238 +        // before this returns with 'MozAppbarDismissing' above.
   1.239 +        StartUI.chromeWin.ContextUI.dismissContextAppbar();
   1.240 +      break;
   1.241 +    }
   1.242 +  },
   1.243 +
   1.244 +  // nsINavHistoryObserver & helpers
   1.245 +
   1.246 +  onBeginUpdateBatch: function() {
   1.247 +    // Avoid heavy grid redraws while a batch is in process
   1.248 +    this._inBatch++;
   1.249 +  },
   1.250 +
   1.251 +  onEndUpdateBatch: function() {
   1.252 +    this.populateGrid(true);
   1.253 +    if (this._inBatch > 0) {
   1.254 +      this._inBatch--;
   1.255 +      this._set.arrangeItems();
   1.256 +    }
   1.257 +  },
   1.258 +
   1.259 +  onVisit: function(aURI, aVisitID, aTime, aSessionID,
   1.260 +                    aReferringID, aTransitionType) {
   1.261 +    if (!this._inBatch) {
   1.262 +      this.populateGrid(true);
   1.263 +    }
   1.264 +  },
   1.265 +
   1.266 +  onTitleChanged: function(aURI, aPageTitle) {
   1.267 +    let changedItems = this._set.getItemsByUrl(aURI.spec);
   1.268 +    for (let item of changedItems) {
   1.269 +      item.setAttribute("label", aPageTitle);
   1.270 +    }
   1.271 +  },
   1.272 +
   1.273 +  onDeleteURI: function(aURI) {
   1.274 +    this.removeHistory(aURI.spec);
   1.275 +  },
   1.276 +
   1.277 +  onClearHistory: function() {
   1.278 +    if ('clearAll' in this._set)
   1.279 +      this._set.clearAll();
   1.280 +  },
   1.281 +
   1.282 +  onPageChanged: function(aURI, aWhat, aValue) {
   1.283 +    if (aWhat ==  Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
   1.284 +      let changedItems = this._set.getItemsByUrl(aURI.spec);
   1.285 +      for (let item of changedItems) {
   1.286 +        let currIcon = item.getAttribute("iconURI");
   1.287 +        if (currIcon != aValue) {
   1.288 +          item.setAttribute("iconURI", aValue);
   1.289 +          if ("refresh" in item)
   1.290 +            item.refresh();
   1.291 +        }
   1.292 +      }
   1.293 +    }
   1.294 +  },
   1.295 +
   1.296 +  onDeleteVisits: function (aURI, aVisitTime, aGUID, aReason, aTransitionType) {
   1.297 +    if ((aReason ==  Ci.nsINavHistoryObserver.REASON_DELETED) && !this._inBatch) {
   1.298 +      this.populateGrid(true);
   1.299 +    }
   1.300 +  },
   1.301 +
   1.302 +  QueryInterface: function(iid) {
   1.303 +    if (iid.equals(Components.interfaces.nsINavHistoryObserver) ||
   1.304 +        iid.equals(Components.interfaces.nsISupports)) {
   1.305 +      return this;
   1.306 +    }
   1.307 +    throw Cr.NS_ERROR_NO_INTERFACE;
   1.308 +  }
   1.309 +});
   1.310 +
   1.311 +let HistoryStartView = {
   1.312 +  _view: null,
   1.313 +  get _grid() { return document.getElementById("start-history-grid"); },
   1.314 +
   1.315 +  init: function init() {
   1.316 +    this._view = new HistoryView(this._grid, true);
   1.317 +    this._view.populateGrid();
   1.318 +    this._grid.removeAttribute("fade");
   1.319 +  },
   1.320 +
   1.321 +  uninit: function uninit() {
   1.322 +    if (this._view) {
   1.323 +      this._view.destruct();
   1.324 +    }
   1.325 +  }
   1.326 +};

mercurial