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