ribbon.js revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Scrollable thumbnail ribbon at the bottom of the Gallery in the Slide mode.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Document} document Document.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MetadataCache} metadataCache MetadataCache instance.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {cr.ui.ArrayDataModel} dataModel Data model.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {cr.ui.ListSelectionModel} selectionModel Selection model.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(string)} onThumbnailError Thumbnail load error handler.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Element} Ribbon element.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function Ribbon(document, metadataCache, dataModel, selectionModel,
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                onThumbnailError) {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = document.createElement('div');
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Ribbon.decorate(self,
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      metadataCache, dataModel, selectionModel, onThumbnailError);
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return self;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Inherit from HTMLDivElement.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.__proto__ = HTMLDivElement.prototype;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Decorate a Ribbon instance.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Ribbon} self Self pointer.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MetadataCache} metadataCache MetadataCache instance.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {cr.ui.ArrayDataModel} dataModel Data model.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {cr.ui.ListSelectionModel} selectionModel Selection model.
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(string)} onThumbnailError Thumbnail load error handler.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.decorate = function(self, metadataCache, dataModel, selectionModel,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           onThumbnailError) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.__proto__ = Ribbon.prototype;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.metadataCache_ = metadataCache;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.dataModel_ = dataModel;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.selectionModel_ = selectionModel;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.onThumbnailError_ = onThumbnailError;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.className = 'ribbon';
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Max number of thumbnails in the ribbon.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {Number}
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.ITEMS_COUNT = 5;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Force redraw the ribbon.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.redraw = function() {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.onSelection_();
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Clear all cached data to force full redraw on the next selection change.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.reset = function() {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.renderCache_ = {};
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.firstVisibleIndex_ = 0;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.lastVisibleIndex_ = -1;  // Zero thumbnails
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Enable the ribbon.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.enable = function() {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.onContentBound_ = this.onContentChange_.bind(this);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dataModel_.addEventListener('content', this.onContentBound_);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.onSpliceBound_ = this.onSplice_.bind(this);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dataModel_.addEventListener('splice', this.onSpliceBound_);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.onSelectionBound_ = this.onSelection_.bind(this);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.selectionModel_.addEventListener('change', this.onSelectionBound_);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.reset();
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.redraw();
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Disable ribbon.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.disable = function() {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dataModel_.removeEventListener('content', this.onContentBound_);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dataModel_.removeEventListener('splice', this.onSpliceBound_);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.selectionModel_.removeEventListener('change', this.onSelectionBound_);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.removeVanishing_();
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.textContent = '';
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Data model splice handler.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Event} event Event.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.onSplice_ = function(event) {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event.removed.length == 0)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event.removed.length > 1) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    console.error('Cannot remove multiple items');
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var removed = this.renderCache_[event.removed[0].getUrl()];
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!removed || !removed.parentNode || !removed.hasAttribute('selected')) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    console.error('Can only remove the selected item');
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var persistentNodes = this.querySelectorAll('.ribbon-image:not([vanishing])');
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.lastVisibleIndex_ < this.dataModel_.length) { // Not at the end.
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var lastNode = persistentNodes[persistentNodes.length - 1];
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (lastNode.nextSibling) {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Pull back a vanishing node from the right.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lastNode.nextSibling.removeAttribute('vanishing');
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Push a new item at the right end.
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.appendChild(this.renderThumbnail_(this.lastVisibleIndex_));
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No items to the right, move the window to the left.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lastVisibleIndex_--;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.firstVisibleIndex_) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.firstVisibleIndex_--;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var firstNode = persistentNodes[0];
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (firstNode.previousSibling) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Pull back a vanishing node from the left.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        firstNode.previousSibling.removeAttribute('vanishing');
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Push a new item at the left end.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var newThumbnail = this.renderThumbnail_(this.firstVisibleIndex_);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newThumbnail.style.marginLeft = -(this.clientHeight - 2) + 'px';
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(newThumbnail, this.firstChild);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        setTimeout(function() {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newThumbnail.style.marginLeft = '0';
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }, 0);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  removed.removeAttribute('selected');
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  removed.setAttribute('vanishing', 'smooth');
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.scheduleRemove_();
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Selection change handler.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.onSelection_ = function() {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var indexes = this.selectionModel_.selectedIndexes;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (indexes.length == 0)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Ignore temporary empty selection.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var selectedIndex = indexes[0];
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var length = this.dataModel_.length;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(dgozman): use margin instead of 2 here.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var itemWidth = this.clientHeight - 2;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var fullItems = Ribbon.ITEMS_COUNT;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fullItems = Math.min(fullItems, length);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var right = Math.floor((fullItems - 1) / 2);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var fullWidth = fullItems * itemWidth;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.style.width = fullWidth + 'px';
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var lastIndex = selectedIndex + right;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  lastIndex = Math.max(lastIndex, fullItems - 1);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  lastIndex = Math.min(lastIndex, length - 1);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var firstIndex = lastIndex - fullItems + 1;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.firstVisibleIndex_ != firstIndex ||
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.lastVisibleIndex_ != lastIndex) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.lastVisibleIndex_ == -1) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.firstVisibleIndex_ = firstIndex;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.lastVisibleIndex_ = lastIndex;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.removeVanishing_();
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.textContent = '';
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var startIndex = Math.min(firstIndex, this.firstVisibleIndex_);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // All the items except the first one treated equally.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var index = startIndex + 1;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         index <= Math.max(lastIndex, this.lastVisibleIndex_);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++index) {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Only add items that are in either old or the new viewport.
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.lastVisibleIndex_ < index && index < firstIndex ||
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          lastIndex < index && index < this.firstVisibleIndex_)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var box = this.renderThumbnail_(index);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      box.style.marginLeft = '0';
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.appendChild(box);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index < firstIndex || index > lastIndex) {
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the node is not in the new viewport we only need it while
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // the animation is playing out.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        box.setAttribute('vanishing', 'slide');
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var slideCount = this.childNodes.length + 1 - Ribbon.ITEMS_COUNT;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var margin = itemWidth * slideCount;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var startBox = this.renderThumbnail_(startIndex);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (startIndex == firstIndex) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Sliding to the right.
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      startBox.style.marginLeft = -margin + 'px';
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.firstChild)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(startBox, this.firstChild);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.appendChild(startBox);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTimeout(function() {
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        startBox.style.marginLeft = '0';
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }, 0);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Sliding to the left. Start item will become invisible and should be
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // removed afterwards.
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      startBox.setAttribute('vanishing', 'slide');
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      startBox.style.marginLeft = '0';
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.firstChild)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(startBox, this.firstChild);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.appendChild(startBox);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTimeout(function() {
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        startBox.style.marginLeft = -margin + 'px';
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }, 0);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.setClass(this, 'fade-left',
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        firstIndex > 0 && selectedIndex != firstIndex);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.setClass(this, 'fade-right',
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lastIndex < length - 1 && selectedIndex != lastIndex);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.firstVisibleIndex_ = firstIndex;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lastVisibleIndex_ = lastIndex;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.scheduleRemove_();
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var oldSelected = this.querySelector('[selected]');
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (oldSelected) oldSelected.removeAttribute('selected');
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var newSelected =
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.renderCache_[this.dataModel_.item(selectedIndex).getUrl()];
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (newSelected) newSelected.setAttribute('selected', true);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Schedule the removal of thumbnails marked as vanishing.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.scheduleRemove_ = function() {
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.removeTimeout_)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearTimeout(this.removeTimeout_);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.removeTimeout_ = setTimeout(function() {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.removeTimeout_ = null;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.removeVanishing_();
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this), 200);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Remove all thumbnails marked as vanishing.
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.removeVanishing_ = function() {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.removeTimeout_) {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearTimeout(this.removeTimeout_);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.removeTimeout_ = 0;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var vanishingNodes = this.querySelectorAll('[vanishing]');
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i != vanishingNodes.length; i++) {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    vanishingNodes[i].removeAttribute('vanishing');
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.removeChild(vanishingNodes[i]);
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Create a DOM element for a thumbnail.
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} index Item index.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Element} Newly created element.
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.renderThumbnail_ = function(index) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var item = this.dataModel_.item(index);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var url = item.getUrl();
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var cached = this.renderCache_[url];
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (cached)
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return cached;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var thumbnail = this.ownerDocument.createElement('div');
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  thumbnail.className = 'ribbon-image';
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  thumbnail.addEventListener('click', function() {
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var index = this.dataModel_.indexOf(item);
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.selectionModel_.unselectAll();
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.selectionModel_.setIndexSelected(index, true);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this));
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  util.createChild(thumbnail, 'image-wrapper');
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.metadataCache_.get(url, Gallery.METADATA_TYPE,
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.setThumbnailImage_.bind(this, thumbnail, url));
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO: Implement LRU eviction.
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Never evict the thumbnails that are currently in the DOM because we rely
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // on this cache to find them by URL.
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.renderCache_[url] = thumbnail;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return thumbnail;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Set the thumbnail image.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Element} thumbnail Thumbnail element
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} url Image url.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} metadata Metadata.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.setThumbnailImage_ = function(thumbnail, url, metadata) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new ThumbnailLoader(url, metadata).load(
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      thumbnail.querySelector('.image-wrapper'),
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      true /* fill */,
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      null /* success callback */,
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.onThumbnailError_.bind(null, url));
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Content change handler.
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Event} event Event.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.onContentChange_ = function(event) {
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var url = event.item.getUrl();
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.remapCache_(event.oldUrl, url);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var thumbnail = this.renderCache_[url];
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (thumbnail && event.metadata)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.setThumbnailImage_(thumbnail, url, event.metadata);
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update the thumbnail element cache.
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} oldUrl Old url.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} newUrl New url.
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Ribbon.prototype.remapCache_ = function(oldUrl, newUrl) {
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (oldUrl != newUrl && (oldUrl in this.renderCache_)) {
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.renderCache_[newUrl] = this.renderCache_[oldUrl];
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete this.renderCache_[oldUrl];
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
368