_synchronize: function ListView_synchronize()

in src/Clients/Web/winjs/js/winjs.js [47322:47523]


                _synchronize: function ListView_synchronize() {
                    var updater = this._updater;
                    this._updater = null;
                    this._groupsChanged = false;

                    this._countDifference = this._countDifference || 0;

                    if (updater && updater.changed) {
                        if (updater.itemsMoved) {
                            this._layout.itemsMoved && this._layout.itemsMoved();
                        }
                        if (updater.removed.length) {
                            this._layout.itemsRemoved && this._layout.itemsRemoved(updater.removed.map(function (node) {
                                return node.itemBox;
                            }));
                        }

                        if (updater.itemsMoved || updater.removed.length || Object.keys(this._insertedItems).length) {
                            this._layout.setupAnimations && this._layout.setupAnimations();
                        }

                        if (this._currentMode().onDataChanged) {
                            this._currentMode().onDataChanged();
                        }

                        var newSelection = [];
                        for (var i in updater.selectionFirst) {
                            if (updater.selectionFirst.hasOwnProperty(i)) {
                                var range = updater.selectionFirst[i];
                                updater.selectionChanged = updater.selectionChanged || ((range.newLastIndex - range.newFirstIndex) !== (range.oldLastIndex - range.oldFirstIndex));
                                if (range.newFirstIndex <= range.newLastIndex) {
                                    newSelection.push({
                                        firstIndex: range.newFirstIndex,
                                        lastIndex: range.newLastIndex
                                    });
                                }
                            }
                        }

                        if (updater.selectionChanged) {
                            var newSelectionItems = new _SelectionManager._Selection(this, newSelection);

                            // We do not allow listeners to cancel the selection
                            // change because the cancellation would also have to
                            // prevent the deletion.
                            this._selection._fireSelectionChanging(newSelectionItems);
                            this._selection._selected.set(newSelection);
                            this._selection._fireSelectionChanged();
                            newSelectionItems.clear();
                        } else {
                            this._selection._selected.set(newSelection);
                        }
                        this._selection._updateCount(this._cachedCount);
                        updater.newSelectionPivot = Math.min(this._cachedCount - 1, updater.newSelectionPivot);
                        this._selection._pivot = (updater.newSelectionPivot >= 0 ? updater.newSelectionPivot : _Constants._INVALID_INDEX);

                        if (updater.newFocus.type !== _UI.ObjectType.groupHeader) {
                            updater.newFocus.index = Math.max(0, Math.min(this._cachedCount - 1, updater.newFocus.index));
                        }
                        this._selection._setFocused(updater.newFocus, this._selection._keyboardFocused());

                        // If there are 2 edits before layoutAnimations runs we need to merge the 2 groups of modified elements.
                        // For example:
                        // If you start with A, B, C and add item Z to the beginning you will have
                        // [ -1 -> 0, 0 -> 1, 1 -> 2, 2 -> 3]
                        // However before layout is called an insert of Y to the beginning also happens you should get
                        // [ -1 -> 0, -1 -> 1, 0 -> 2, 1 -> 3, 2 -> 4]
                        var previousModifiedElements = this._modifiedElements || [];
                        var previousModifiedElementsHash = {};
                        this._modifiedElements = [];
                        this._countDifference += updater.countDifference;

                        for (i = 0; i < previousModifiedElements.length; i++) {
                            var modifiedElement = previousModifiedElements[i];
                            if (modifiedElement.newIndex === -1) {
                                this._modifiedElements.push(modifiedElement);
                            } else {
                                previousModifiedElementsHash[modifiedElement.newIndex] = modifiedElement;
                            }
                        }

                        for (i = 0; i < updater.removed.length; i++) {
                            var removed = updater.removed[i];
                            var modifiedElement = previousModifiedElementsHash[removed.index];
                            if (modifiedElement) {
                                delete previousModifiedElementsHash[removed.index];
                            } else {
                                modifiedElement = {
                                    oldIndex: removed.index
                                };
                            }
                            modifiedElement.newIndex = -1;
                            if (!modifiedElement._removalHandled) {
                                modifiedElement._itemBox = removed.itemBox;
                                modifiedElement._containerStripe = removed.containerStripe;
                            }
                            this._modifiedElements.push(modifiedElement);
                        }

                        var insertedKeys = Object.keys(this._insertedItems);
                        for (i = 0; i < insertedKeys.length; i++) {
                            this._modifiedElements.push({
                                oldIndex: -1,
                                newIndex: this._insertedItems[insertedKeys[i]].index
                            });
                        }

                        this._writeProfilerMark("_synchronize:update_modifiedElements,StartTM");
                        var newItems = {};
                        for (i in updater.elements) {
                            if (updater.elements.hasOwnProperty(i)) {
                                var elementInfo = updater.elements[i];
                                newItems[elementInfo.newIndex] = {
                                    element: elementInfo.item,
                                    container: elementInfo.container,
                                    itemBox: elementInfo.itemBox,
                                    itemsManagerRecord: elementInfo.itemsManagerRecord,
                                    detached: elementInfo.detached
                                };

                                var modifiedElement = previousModifiedElementsHash[elementInfo.index];
                                if (modifiedElement) {
                                    delete previousModifiedElementsHash[elementInfo.index];
                                    modifiedElement.newIndex = elementInfo.newIndex;
                                } else {
                                    modifiedElement = {
                                        oldIndex: elementInfo.index,
                                        newIndex: elementInfo.newIndex
                                    };
                                }
                                modifiedElement.moved = elementInfo.moved;
                                this._modifiedElements.push(modifiedElement);
                            }
                        }
                        this._writeProfilerMark("_synchronize:update_modifiedElements,StopTM");

                        var previousIndices = Object.keys(previousModifiedElementsHash);
                        for (i = 0; i < previousIndices.length; i++) {
                            var key = previousIndices[i];
                            var modifiedElement = previousModifiedElementsHash[key];
                            if (modifiedElement.oldIndex !== -1) {
                                this._modifiedElements.push(modifiedElement);
                            }
                        }

                        this._view.items._itemData = newItems;
                        if (updater.updateDrag && this._currentMode()._dragging) {
                            if (!this._currentMode()._draggingUnselectedItem) {
                                this._currentMode()._dragInfo = this._selection;
                            } else if (updater.newDragInfo) {
                                this._currentMode()._dragInfo = updater.newDragInfo;
                            }
                            this._currentMode().fireDragUpdateEvent();
                        }

                        // If the focused item is removed, or the item we're trying to focus on has been moved before we can focus on it,
                        // we need to update our focus request to get the item from the appropriate index.
                        if (updater.focusedItemRemoved || (this._focusRequest && (updater.oldFocus.index !== updater.newFocus.index) || (updater.oldFocus.type !== updater.newFocus.type))) {
                            this._itemFocused = false;
                            this._setFocusOnItem(this._selection._getFocused());
                        } else if (updater.newFocusedItem) {
                            // We need to restore the value of _hasKeyboardFocus because a changed item
                            // gets removed from the DOM at the time of the notification. If the item
                            // had focus at that time, then our value of _hasKeyboardFocus will have changed.
                            this._hasKeyboardFocus = updater.hadKeyboardFocus;
                            this._itemFocused = false;
                            this._setFocusOnItem(this._selection._getFocused());
                        }

                        var that = this;
                        return this._groups.synchronizeGroups().then(function () {
                            if (updater.newFocus.type === _UI.ObjectType.groupHeader) {
                                updater.newFocus.index = Math.min(that._groups.length() - 1, updater.newFocus.index);

                                if (updater.newFocus.index < 0) {
                                    // An empty listview has currentFocus = item 0
                                    updater.newFocus = { type: _UI.ObjectType.item, index: 0 };
                                }
                                that._selection._setFocused(updater.newFocus, that._selection._keyboardFocused());
                            }

                            that._versionManager.endUpdating();
                            if (updater.deletesCount > 0) {
                                that._updateDeleteWrapperSize();
                            }

                            return that._view.updateTree(that._cachedCount, that._countDifference, that._modifiedElements);
                        }).then(function () {
                            return that._lastScrollPosition;
                        });
                    } else {
                        this._countDifference += updater ? updater.countDifference : 0;

                        var that = this;
                        return this._groups.synchronizeGroups().then(function ListView_synchronizeGroups_success_groupsChanged() {
                            updater && that._versionManager.endUpdating();
                            return that._view.updateTree(that._cachedCount, that._countDifference, that._modifiedElements);
                        }).then(function () {
                            return that.scrollPosition;
                        });
                    }
                },