1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * Group of progress item in the progress center panels.
10 * This is responsible for generating the summarized item and managing lifetime
12 * @param {string} name Name of the group.
13 * @param {boolean} quiet Whether the group is for quiet items or not.
16 function ProgressCenterItemGroup(name, quiet) {
24 * Whether the group is for quiet items or not.
32 * @type {ProgressCenterItemGroup.State}
35 this.state_ = ProgressCenterItemGroup.State.EMPTY;
38 * Items that are progressing, or completed but still animated.
40 * @type {Object.<string, ProgressCenterItem>}
46 * Set of animated state of items. Key is item ID and value is whether the
47 * item is animated or not.
48 * @type {Object.<string, boolean>}
54 * Last summarized item.
55 * @type {ProgressCenterItem}
58 this.summarizedItem_ = null;
61 * Whether the summarized item is animated or not.
65 this.summarizedItemAnimated_ = false;
68 * Total maximum progress value of items already completed and removed from
73 this.totalProgressMax_ = 0;
76 * Total progress value of items already completed and removed from
81 this.totalProgressValue_ = 0;
87 * State of ProgressCenterItemGroup.
91 ProgressCenterItemGroup.State = Object.freeze({
92 // Group has no items.
94 // Group has at least 1 progressing item.
96 // Group has no progressing items but still shows error items.
101 * Makes the summarized item for the groups.
103 * When a group has only error items, getSummarizedItem of the item returns
104 * null. Basically the first result of the groups that the progress center panel
105 * contains is used as a summarized item. But If all the group returns null, the
106 * progress center panel generates the summarized item by using the method.
108 * @param {Array.<ProgressCenterItemGroup>} var_groups List of groups.
109 * @return {ProgressCenterItem} Summarized item.
111 ProgressCenterItemGroup.getSummarizedErrorItem = function(var_groups) {
112 var groups = Array.prototype.slice.call(arguments);
114 for (var i = 0; i < groups.length; i++) {
115 for (var id in groups[i].items_) {
116 var item = groups[i].items_[id];
117 if (item.state === ProgressItemState.ERROR)
118 errorItems.push(item);
121 if (errorItems.length === 0)
124 if (errorItems.length === 1)
125 return errorItems[0].clone();
127 var item = new ProgressCenterItem();
128 item.state = ProgressItemState.ERROR;
129 item.message = strf('ERROR_PROGRESS_SUMMARY_PLURAL',
136 * Obtains Whether the item should be animated or not.
137 * @param {boolean} previousAnimated Whether the item is previously animated or
139 * @param {ProgressCenterItem} previousItem Item before updating.
140 * @param {ProgressCenterItem} item New item.
141 * @return {boolean} Whether the item should be animated or not.
144 ProgressCenterItemGroup.shouldAnimate_ = function(
145 previousAnimated, previousItem, item) {
146 if (!previousItem || !item || previousItem.quiet || item.quiet)
148 if (previousItem.progressRateInPercent < item.progressRateInPercent)
150 if (previousAnimated &&
151 previousItem.progressRateInPercent === item.progressRateInPercent)
156 ProgressCenterItemGroup.prototype = {
158 * @return {ProgressCenterItemGroup.State} State of the group.
165 * @return {number} Number of error items that the group contains.
169 for (var id in this.items_) {
170 if (this.items_[id].state === ProgressItemState.ERROR)
178 * Obtains the progressing (or completed but animated) item.
180 * @param {string} id Item ID.
181 * @return {ProgressCenterItem} Item having the ID.
183 ProgressCenterItemGroup.prototype.getItem = function(id) {
184 return this.items_[id] || null;
188 * Obtains whether the item should be animated or not.
189 * @param {string} id Item ID.
190 * @return {boolean} Whether the item should be animated or not.
192 ProgressCenterItemGroup.prototype.isAnimated = function(id) {
193 return !!this.animated_[id];
197 * Obtains whether the summarized item should be animated or not.
198 * @return {boolean} Whether the summarized item should be animated or not.
200 ProgressCenterItemGroup.prototype.isSummarizedAnimated = function() {
201 return this.summarizedItemAnimated_;
205 * Starts item update.
206 * Marks the given item as updating.
207 * @param {ProgressCenterItem} item Item containing updated information.
209 ProgressCenterItemGroup.prototype.update = function(item) {
210 // If the group is inactive, go back to the empty state.
213 // Compares the current state and the new state to check if the update is
215 var previousItem = this.items_[item.id];
216 switch (item.state) {
217 case ProgressItemState.ERROR:
218 if (previousItem && previousItem.state !== ProgressItemState.PROGRESSING)
220 if (this.state_ === ProgressCenterItemGroup.State.EMPTY)
221 this.state_ = ProgressCenterItemGroup.State.INACTIVE;
222 this.items_[item.id] = item.clone();
223 this.animated_[item.id] = false;
224 this.summarizedItem_ = null;
227 case ProgressItemState.PROGRESSING:
228 case ProgressItemState.COMPLETED:
229 if ((!previousItem && item.state === ProgressItemState.COMPLETED) ||
231 previousItem.state !== ProgressItemState.PROGRESSING))
233 if (this.state_ === ProgressCenterItemGroup.State.EMPTY)
234 this.state_ = ProgressCenterItemGroup.State.ACTIVE;
235 this.items_[item.id] = item.clone();
236 this.animated_[item.id] = ProgressCenterItemGroup.shouldAnimate_(
237 !!this.animated_[item.id],
240 if (!this.animated_[item.id])
241 this.completeItemAnimation(item.id);
244 case ProgressItemState.CANCELED:
246 previousItem.state !== ProgressItemState.PROGRESSING)
248 delete this.items_[item.id];
249 this.animated_[item.id] = false;
250 this.summarizedItem_ = null;
253 // Update the internal summarized item cache.
254 var previousSummarizedItem = this.summarizedItem_;
255 this.summarizedItem_ = this.getSummarizedItem(0);
256 this.summarizedItemAnimated_ = ProgressCenterItemGroup.shouldAnimate_(
257 !!this.summarizedItemAnimated_,
258 previousSummarizedItem,
259 this.summarizedItem_);
260 if (!this.summarizedItemAnimated_)
261 this.completeSummarizedItemAnimation();
265 * Notifies the end of the item's animation to the group.
266 * If all the items except error items completes, the group enter the inactive
268 * @param {string} id Item ID.
270 ProgressCenterItemGroup.prototype.completeItemAnimation = function(id) {
271 if (this.state_ !== ProgressCenterItemGroup.State.ACTIVE)
274 this.animated_[id] = false;
275 if (this.items_[id].state === ProgressItemState.COMPLETED) {
276 this.totalProgressValue_ += (this.items_[id].progressValue || 0.0);
277 this.totalProgressMax_ += (this.items_[id].progressMax || 0.0);
278 delete this.items_[id];
279 this.tryEndActive_();
284 * Notifies the end of the summarized item's animation.
285 * This may update summarized view. (1 progressing + 1 error -> 1 error)
287 ProgressCenterItemGroup.prototype.completeSummarizedItemAnimation = function() {
288 this.summarizedItemAnimated_ = false;
289 this.tryEndActive_();
293 * Obtains the summary of the set.
294 * @param {number} numOtherErrors Number of errors contained by other groups.
295 * @return {ProgressCenterItem} Item.
297 ProgressCenterItemGroup.prototype.getSummarizedItem =
298 function(numOtherErrors) {
299 if (this.state_ === ProgressCenterItemGroup.State.EMPTY ||
300 this.state_ === ProgressCenterItemGroup.State.INACTIVE)
303 var summarizedItem = new ProgressCenterItem();
304 summarizedItem.quiet = this.quiet_;
305 summarizedItem.progressMax += this.totalProgressMax_;
306 summarizedItem.progressValue += this.totalProgressValue_;
307 var progressingItems = [];
311 for (var id in this.items_) {
312 var item = this.items_[id];
316 switch (item.state) {
317 case ProgressItemState.PROGRESSING:
318 case ProgressItemState.COMPLETED:
319 progressingItems.push(item);
321 case ProgressItemState.ERROR:
322 errorItems.push(item);
326 // If all of the progressing items have the same type, then use
327 // it. Otherwise use TRANSFER, since it is the most generic.
328 if (summarizedItem.type === null)
329 summarizedItem.type = item.type;
330 else if (summarizedItem.type !== item.type)
331 summarizedItem.type = ProgressItemType.TRANSFER;
333 // Sum up the progress values.
334 summarizedItem.progressMax += item.progressMax;
335 summarizedItem.progressValue += item.progressValue;
339 if (progressingItems.length === 1 &&
340 errorItems.length + numOtherErrors === 0) {
341 summarizedItem.id = progressingItems[0].id;
342 summarizedItem.cancelCallback = progressingItems[0].cancelCallback;
343 summarizedItem.message = progressingItems[0].message;
344 summarizedItem.state = progressingItems[0].state;
345 return summarizedItem;
348 // Returns integrated items.
349 if (progressingItems.length > 0) {
350 var numErrors = errorItems.length + numOtherErrors;
352 switch (summarizedItem.type) {
353 case ProgressItemType.COPY:
354 messages.push(str('COPY_PROGRESS_SUMMARY'));
356 case ProgressItemType.MOVE:
357 messages.push(str('MOVE_PROGRESS_SUMMARY'));
359 case ProgressItemType.DELETE:
360 messages.push(str('DELETE_PROGRESS_SUMMARY'));
362 case ProgressItemType.ZIP:
363 messages.push(str('ZIP_PROGRESS_SUMMARY'));
365 case ProgressItemType.SYNC:
366 messages.push(str('SYNC_PROGRESS_SUMMARY'));
368 case ProgressItemType.TRANSFER:
369 messages.push(str('TRANSFER_PROGRESS_SUMMARY'));
373 messages.push(str('ERROR_PROGRESS_SUMMARY'));
374 else if (numErrors > 1)
375 messages.push(strf('ERROR_PROGRESS_SUMMARY_PLURAL', numErrors));
376 summarizedItem.single = false;
377 summarizedItem.message = messages.join(' ');
378 summarizedItem.state = ProgressItemState.PROGRESSING;
379 return summarizedItem;
382 // Returns complete items.
383 summarizedItem.state = ProgressItemState.COMPLETED;
384 return summarizedItem;
388 * Goes back to the EMPTY state from the INACTIVE state. Removes all the items.
389 * If the current state is not the INACTIVE, nothing happens.
391 ProgressCenterItemGroup.prototype.endInactive = function() {
392 if (this.state_ !== ProgressCenterItemGroup.State.INACTIVE)
396 this.summarizedItem_ = null;
397 this.summarizedItemAnimated_ = false;
398 this.totalProgressValue_ = 0.0;
399 this.totalProgressMax_ = 0.0;
400 this.state_ = ProgressCenterItemGroup.State.EMPTY;
404 * Ends active state if there is no progressing and animated items.
407 ProgressCenterItemGroup.prototype.tryEndActive_ = function() {
408 if (this.state_ !== ProgressCenterItemGroup.State.ACTIVE ||
409 this.summarizedItemAnimated_)
411 var hasError = false;
412 for (var id in this.items_) {
413 // If there is non-error item (progressing, or completed but still
414 // animated), we should stay the active state.
415 if (this.items_[id].state !== ProgressItemState.ERROR)
419 this.state_ = ProgressCenterItemGroup.State.INACTIVE;