Skip to content

Instantly share code, notes, and snippets.

@pingyen
Created September 20, 2012 10:06
Show Gist options
  • Save pingyen/3755043 to your computer and use it in GitHub Desktop.
Save pingyen/3755043 to your computer and use it in GitHub Desktop.
WinJS.UI ui.js
This file has been truncated, but you can view the full file.
/// <loc filename="Metadata\ui_loc_oam.xml" format="messagebundle" />
/*!
© Microsoft. All rights reserved.
This library is supported for use in Windows Store apps only.
Build: 1.0.9200.20498.win8_ldr.120817-1716
Version: Microsoft.WinJS.1.0
*/
/// <reference path="ms-appx://Microsoft.WinJS.1.0/js/base.js" />
/// <reference path="ms-appx://Microsoft.WinJS.1.0/js/ui.js" />
(function animationsInit(WinJS) {
"use strict";
var thisWinUI = WinJS.UI;
var mstransform = "transform";
// Default to 11 pixel from the left (or right if RTL)
var defaultOffset = [{ top: "0px", left: "11px", rtlflip: true }];
var OffsetArray = WinJS.Class.define (function OffsetArray_ctor(offset, keyframe, defOffset) {
// Constructor
defOffset = defOffset || defaultOffset;
if (Array.isArray(offset) && offset.length > 0) {
this.offsetArray = offset;
if (offset.length === 1) {
this.keyframe = checkKeyframe(offset[0], defOffset[0], keyframe);
}
} else if (offset && offset.hasOwnProperty("top") && offset.hasOwnProperty("left")) {
this.offsetArray = [offset];
this.keyframe = checkKeyframe(offset, defOffset[0], keyframe);
} else {
this.offsetArray = defOffset;
this.keyframe = chooseKeyframe(defOffset[0], keyframe);
}
}, { // Public Members
getOffset: function (i) {
if (i >= this.offsetArray.length) {
i = this.offsetArray.length - 1;
}
return this.offsetArray[i];
}
}, { // Static Members
supportedForProcessing: false,
});
function checkKeyframe(offset, defOffset, keyframe) {
if (!keyframe ||
offset.left !== defOffset.left ||
offset.top !== defOffset.top ||
(offset.rtlflip && !defOffset.rtlflip)) {
return null;
}
if (!offset.rtlflip) {
return keyframe;
}
return keyframeCallback(keyframe);
}
function chooseKeyframe(defOffset, keyframe) {
if (!keyframe || !defOffset.rtlflip) {
return keyframe;
}
return keyframeCallback(keyframe);
}
function keyframeCallback(keyframe) {
var keyframeRtl = keyframe + "-rtl";
return function (i, elem) {
return window.getComputedStyle(elem).direction === "ltr" ? keyframe : keyframeRtl;
}
}
function makeArray(elements)
{
if (Array.isArray(elements) || elements instanceof NodeList || elements instanceof HTMLCollection) {
return elements;
} else if (elements) {
return [elements];
} else {
return [];
}
}
var isSnapped = function() {
if (WinJS.Utilities.hasWinRT) {
var appView = Windows.UI.ViewManagement.ApplicationView;
var snapped = Windows.UI.ViewManagement.ApplicationViewState.snapped;
isSnapped = function() {
return appView.value === snapped;
};
} else {
isSnapped = function() { return false; }
}
return isSnapped();
};
function collectOffsetArray(elemArray) {
var offsetArray = [];
for (var i = 0; i < elemArray.length; i++) {
var offset = {
top: elemArray[i].offsetTop,
left: elemArray[i].offsetLeft
};
var matrix = window.getComputedStyle(elemArray[i], null).transform.split(",");
if (matrix.length === 6) {
offset.left += parseFloat(matrix[4]);
offset.top += parseFloat(matrix[5]);
}
offsetArray.push(offset);
}
return offsetArray;
}
function staggerDelay(initialDelay, extraDelay, delayFactor, delayCap) {
return function (i) {
var ret = initialDelay;
for (var j = 0; j < i; j++) {
extraDelay *= delayFactor;
ret += extraDelay;
}
if (delayCap) {
ret = Math.min(ret, delayCap);
}
return ret;
};
}
function makeOffsetsRelative(elemArray, offsetArray) {
for (var i = 0; i < offsetArray.length; i++) {
offsetArray[i].top -= elemArray[i].offsetTop;
offsetArray[i].left -= elemArray[i].offsetLeft;
}
}
function animTranslate2DTransform(elemArray, offsetArray, transition) {
makeOffsetsRelative(elemArray, offsetArray);
for (var i = 0; i < elemArray.length; i++) {
if (offsetArray[i].top !== 0 || offsetArray[i].left !== 0) {
elemArray[i].style.transform = "translate(" + offsetArray[i].left + "px, " + offsetArray[i].top + "px)";
}
}
return thisWinUI.executeTransition(elemArray, transition);
}
function translateCallback(offsetArray, prefix) {
prefix = prefix || "";
return function (i, elem) {
var offset = offsetArray.getOffset(i);
var left = offset.left;
if (offset.rtlflip && window.getComputedStyle(elem).direction === "rtl") {
left = left.toString();
if (left.charAt(0) === "-") {
left = left.substring(1);
} else {
left = "-" + left;
}
}
return prefix + "translate(" + left + ", " + offset.top + ")";
};
}
function translateCallbackAnimate(offsetArray, suffix)
{
suffix = suffix || "";
return function (i, elem) {
var offset = offsetArray[i];
return "translate(" + offset.left + "px, " + offset.top + "px) " + suffix;
};
}
function keyframeCallbackAnimate(offsetArray, keyframe)
{
return function (i, elem) {
var offset = offsetArray[i];
return (offset.left === 0 && offset.top === 0) ? keyframe : null;
};
}
function layoutTransition(LayoutTransition, target, affected, extra)
{
var targetArray = makeArray(target);
var affectedArray = makeArray(affected);
var offsetArray = collectOffsetArray(affectedArray);
return new LayoutTransition(targetArray, affectedArray, offsetArray, extra);
}
var ExpandAnimation = WinJS.Class.define(function ExpandAnimation_ctor(revealedArray, affectedArray, offsetArray) {
// Constructor
this.revealedArray = revealedArray;
this.affectedArray = affectedArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
var promise1 = thisWinUI.executeAnimation(
this.revealedArray,
{
keyframe: "WinJS-opacity-in",
property: "opacity",
delay: this.affectedArray.length > 0 ? 200 : 0,
duration: 167,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: 0,
to: 1
});
var promise2 = animTranslate2DTransform(
this.affectedArray,
this.offsetArray,
{
property: mstransform,
delay: 0,
duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
return WinJS.Promise.join([promise1, promise2]);
}
}, { // Static Members
supportedForProcessing: false,
});
var CollapseAnimation = WinJS.Class.define(function CollapseAnimation_ctor(hiddenArray, affectedArray, offsetArray) {
// Constructor
this.hiddenArray = hiddenArray;
this.affectedArray = affectedArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
var promise1 = thisWinUI.executeAnimation(
this.hiddenArray,
{
keyframe: "WinJS-opacity-out",
property: "opacity",
delay: 0,
duration: 167,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: 1,
to: 0
});
var promise2 = animTranslate2DTransform(
this.affectedArray,
this.offsetArray,
{
property: mstransform,
delay: this.hiddenArray.length > 0 ? 167 : 0,
duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
return WinJS.Promise.join([promise1, promise2]);
}
}, { // Static Members
supportedForProcessing: false,
});
var RepositionAnimation = WinJS.Class.define(function RepositionAnimation_ctor(target, elementArray, offsetArray) {
// Constructor
this.elementArray = elementArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
return animTranslate2DTransform(
this.elementArray,
this.offsetArray,
{
property: mstransform,
delay : staggerDelay(0, 33, 1, 250),
duration : 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
}
}, { // Static Members
supportedForProcessing: false,
});
var AddToListAnimation = WinJS.Class.define(function AddToListAnimation_ctor(addedArray, affectedArray, offsetArray) {
// Constructor
this.addedArray = addedArray;
this.affectedArray = affectedArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
var delay = this.affectedArray.length > 0 ? 240 : 0;
var promise1 = thisWinUI.executeAnimation(
this.addedArray,
[{
keyframe: "WinJS-scale-up",
property: mstransform,
delay: delay,
duration: 120,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: "scale(0.85)",
to: "none"
},
{
keyframe: "WinJS-opacity-in",
property: "opacity",
delay: delay,
duration: 120,
timing: "linear",
from: 0,
to: 1
}]
);
var promise2 = animTranslate2DTransform(
this.affectedArray,
this.offsetArray,
{
property: mstransform,
delay: 0,
duration: 400,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
return WinJS.Promise.join([promise1, promise2]);
}
}, { // Static Members
supportedForProcessing: false,
});
var DeleteFromListAnimation = WinJS.Class.define(function DeleteFromListAnimation_ctor(deletedArray, remainingArray, offsetArray) {
// Constructor
this.deletedArray = deletedArray;
this.remainingArray = remainingArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
var promise1 = thisWinUI.executeAnimation(
this.deletedArray,
[{
keyframe: "WinJS-scale-down",
property: mstransform,
delay: 0,
duration: 120,
timing: "cubic-bezier(0.11, 0.5, 0.24, .96)",
from: "none",
to: "scale(0.85)"
},
{
keyframe: "WinJS-opacity-out",
property: "opacity",
delay: 0,
duration: 120,
timing: "linear",
from: 1,
to: 0
}]);
var promise2 = animTranslate2DTransform(
this.remainingArray,
this.offsetArray,
{
property: mstransform,
delay: this.deletedArray.length > 0 ? 60 : 0,
duration: 400,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
return WinJS.Promise.join([promise1, promise2]);
}
}, { // Static Members
supportedForProcessing: false,
});
var _UpdateListAnimation = WinJS.Class.define(function _UpdateListAnimation_ctor(addedArray, affectedArray, offsetArray, deleted) {
// Constructor
this.addedArray = addedArray;
this.affectedArray = affectedArray;
this.offsetArray = offsetArray;
var deletedArray = makeArray(deleted);
this.deletedArray = deletedArray;
this.deletedOffsetArray = collectOffsetArray(deletedArray);
},{ // Public Members
execute: function () {
makeOffsetsRelative(this.deletedArray, this.deletedOffsetArray);
var delay = 0;
var promise1 = thisWinUI.executeAnimation(
this.deletedArray,
[{
keyframe: keyframeCallbackAnimate(this.deletedOffsetArray, "WinJS-scale-down"),
property: mstransform,
delay: 0,
duration: 120,
timing: "cubic-bezier(0.11, 0.5, 0.24, .96)",
from: translateCallbackAnimate(this.deletedOffsetArray),
to: translateCallbackAnimate(this.deletedOffsetArray, "scale(0.85)")
},
{
keyframe: "WinJS-opacity-out",
property: "opacity",
delay: 0,
duration: 120,
timing: "linear",
from: 1,
to: 0
}]);
if (this.deletedArray.length > 0) {
delay += 60;
}
var promise2 = animTranslate2DTransform(
this.affectedArray,
this.offsetArray,
{
property: mstransform,
delay: delay,
duration: 400,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
if (this.affectedArray.length > 0) {
delay += 240;
} else if (delay) {
delay += 60;
}
var promise3 = thisWinUI.executeAnimation(
this.addedArray,
[{
keyframe: "WinJS-scale-up",
property: mstransform,
delay: delay,
duration: 120,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: "scale(0.85)",
to: "none"
},
{
keyframe: "WinJS-opacity-in",
property: "opacity",
delay: delay,
duration: 120,
timing: "linear",
from: 0,
to: 1
}]
);
return WinJS.Promise.join([promise1, promise2, promise3]);
}
}, { // Static Members
supportedForProcessing: false,
});
var AddToSearchListAnimation = WinJS.Class.define(function AddToSearchListAnimation_ctor(addedArray, affectedArray, offsetArray) {
// Constructor
this.addedArray = addedArray;
this.affectedArray = affectedArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
var promise1 = thisWinUI.executeAnimation(
this.addedArray,
{
keyframe: "WinJS-opacity-in",
property: "opacity",
delay: this.affectedArray.length > 0 ? 240 : 0,
duration: 117,
timing: "linear",
from: 0,
to: 1
});
var promise2 = animTranslate2DTransform(
this.affectedArray,
this.offsetArray,
{
property: mstransform,
delay: 0,
duration: 400,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
return WinJS.Promise.join([promise1, promise2]);
}
}, { // Static Members
supportedForProcessing: false,
});
var DeleteFromSearchListAnimation = WinJS.Class.define(function DeleteFromSearchListAnimation_ctor(deletedArray, remainingArray, offsetArray) {
// Constructor
this.deletedArray = deletedArray;
this.remainingArray = remainingArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
var promise1 = thisWinUI.executeAnimation(
this.deletedArray,
{
keyframe: "WinJS-opacity-out",
property: "opacity",
delay: 0,
duration: 93,
timing: "linear",
from: 1,
to: 0
});
var promise2 = animTranslate2DTransform(
this.remainingArray,
this.offsetArray,
{
property: mstransform,
delay: this.deletedArray.length > 0 ? 60 : 0,
duration: 400,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
return WinJS.Promise.join([promise1, promise2]);
}
}, { // Static Members
supportedForProcessing: false,
});
var PeekAnimation = WinJS.Class.define(function PeekAnimation_ctor(target, elementArray, offsetArray) {
// Constructor
this.elementArray = elementArray;
this.offsetArray = offsetArray;
},{ // Public Members
execute: function () {
return animTranslate2DTransform(
this.elementArray,
this.offsetArray,
{
property: mstransform,
delay : 0,
duration : 2000,
timing : "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
}
}, { // Static Members
supportedForProcessing: false,
});
WinJS.Namespace.define("WinJS.UI.Animation", {
createExpandAnimation: function (revealed, affected) {
/// <signature helpKeyword="WinJS.UI.Animation.createExpandAnimation">
/// <summary locid="WinJS.UI.Animation.createExpandAnimation">
/// Creates an expand animation.
/// After creating the ExpandAnimation object,
/// modify the document to move the elements to their new positions,
/// then call the execute method on the ExpandAnimation object.
/// </summary>
/// <param name="revealed" locid="WinJS.UI.Animation.createExpandAnimation_p:revealed">
/// Single element or collection of elements which were revealed.
/// </param>
/// <param name="affected" locid="WinJS.UI.Animation.createExpandAnimation_p:affected">
/// Single element or collection of elements whose positions were
/// affected by the expand.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createExpandAnimation_returnValue">
/// ExpandAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(ExpandAnimation, revealed, affected);
},
createCollapseAnimation: function (hidden, affected) {
/// <signature helpKeyword="WinJS.UI.Animation.createCollapseAnimation">
/// <summary locid="WinJS.UI.Animation.createCollapseAnimation">
/// Creates a collapse animation.
/// After creating the CollapseAnimation object,
/// modify the document to move the elements to their new positions,
/// then call the execute method on the CollapseAnimation object.
/// </summary>
/// <param name="hidden" locid="WinJS.UI.Animation.createCollapseAnimation_p:hidden">
/// Single element or collection of elements being removed from view.
/// When the animation completes, the application should hide the elements
/// or remove them from the document.
/// </param>
/// <param name="affected" locid="WinJS.UI.Animation.createCollapseAnimation_p:affected">
/// Single element or collection of elements whose positions were
/// affected by the collapse.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createCollapseAnimation_returnValue">
/// CollapseAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(CollapseAnimation, hidden, affected);
},
createRepositionAnimation: function (element) {
/// <signature helpKeyword="WinJS.UI.Animation.createRepositionAnimation">
/// <summary locid="WinJS.UI.Animation.createRepositionAnimation">
/// Creates a reposition animation.
/// After creating the RepositionAnimation object,
/// modify the document to move the elements to their new positions,
/// then call the execute method on the RepositionAnimation object.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.createRepositionAnimation_p:element">
/// Single element or collection of elements which were repositioned.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createRepositionAnimation_returnValue">
/// RepositionAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(RepositionAnimation, null, element);
},
fadeIn: function (shown) {
/// <signature helpKeyword="WinJS.UI.Animation.fadeIn">
/// <summary locid="WinJS.UI.Animation.fadeIn">
/// Execute a fade-in animation.
/// </summary>
/// <param name="shown" locid="WinJS.UI.Animation.fadeIn_p:element">
/// Single element or collection of elements to fade in.
/// At the end of the animation, the opacity of the elements is 1.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.fadeIn_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
return thisWinUI.executeTransition(
shown,
{
property: "opacity",
delay: 0,
duration: 250,
timing: "linear",
from: 0,
to: 1
});
},
fadeOut: function (hidden) {
/// <signature helpKeyword="WinJS.UI.Animation.fadeOut">
/// <summary locid="WinJS.UI.Animation.fadeOut">
/// Execute a fade-out animation.
/// </summary>
/// <param name="hidden" locid="WinJS.UI.Animation.fadeOut_p:element">
/// Single element or collection of elements to fade out.
/// At the end of the animation, the opacity of the elements is 0.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.fadeOut_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
return thisWinUI.executeTransition(
hidden,
{
property: "opacity",
delay: 0,
duration: 167,
timing: "linear",
to: 0
});
},
createAddToListAnimation: function (added, affected) {
/// <signature helpKeyword="WinJS.UI.Animation.createAddToListAnimation">
/// <summary locid="WinJS.UI.Animation.createAddToListAnimation">
/// Creates an animation for adding to a list.
/// After creating the AddToListAnimation object,
/// modify the document to move the elements to their new positions,
/// then call the execute method on the AddToListAnimation object.
/// </summary>
/// <param name="added" locid="WinJS.UI.Animation.createAddToListAnimation_p:added">
/// Single element or collection of elements which were added.
/// </param>
/// <param name="affected" locid="WinJS.UI.Animation.createAddToListAnimation_p:affected">
/// Single element or collection of elements whose positions were
/// affected by the add.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createAddToListAnimation_returnValue">
/// AddToListAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(AddToListAnimation, added, affected);
},
createDeleteFromListAnimation: function (deleted, remaining) {
/// <signature helpKeyword="WinJS.UI.Animation.createDeleteFromListAnimation">
/// <summary locid="WinJS.UI.Animation.createDeleteFromListAnimation">
/// Crestes an animation for deleting from a list.
/// After creating the DeleteFromListAnimation object,
/// modify the document to reflect the deletion,
/// then call the execute method on the DeleteFromListAnimation object.
/// </summary>
/// <param name="deleted" locid="WinJS.UI.Animation.createDeleteFromListAnimation_p:deleted">
/// Single element or collection of elements which will be deleted.
/// When the animation completes, the application should hide the elements
/// or remove them from the document.
/// </param>
/// <param name="remaining" locid="WinJS.UI.Animation.createDeleteFromListAnimation_p:remaining">
/// Single element or collection of elements whose positions were
/// affected by the deletion.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createDeleteFromListAnimation_returnValue">
/// DeleteFromListAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(DeleteFromListAnimation, deleted, remaining);
},
_createUpdateListAnimation: function (added, deleted, affected) {
return layoutTransition(_UpdateListAnimation, added, affected, deleted);
},
createAddToSearchListAnimation: function (added, affected) {
/// <signature helpKeyword="WinJS.UI.Animation.createAddToSearchListAnimation">
/// <summary locid="WinJS.UI.Animation.createAddToSearchListAnimation">
/// Creates an animation for adding to a list of search results.
/// This is similar to an AddToListAnimation, but faster.
/// After creating the AddToSearchListAnimation object,
/// modify the document to move the elements to their new positions,
/// then call the execute method on the AddToSearchListAnimation object.
/// </summary>
/// <param name="added" locid="WinJS.UI.Animation.createAddToSearchListAnimation_p:added">
/// Single element or collection of elements which were added.
/// </param>
/// <param name="affected" locid="WinJS.UI.Animation.createAddToSearchListAnimation_p:affected">
/// Single element or collection of elements whose positions were
/// affected by the add.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createAddToSearchListAnimation_returnValue">
/// AddToSearchListAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(AddToSearchListAnimation, added, affected);
},
createDeleteFromSearchListAnimation: function (deleted, remaining) {
/// <signature helpKeyword="WinJS.UI.Animation.createDeleteFromSearchListAnimation">
/// <summary locid="WinJS.UI.Animation.createDeleteFromSearchListAnimation">
/// Creates an animation for deleting from a list of search results.
/// This is similar to an DeleteFromListAnimation, but faster.
/// After creating the DeleteFromSearchListAnimation object,
/// modify the document to move the elements to their new positions,
/// then call the execute method on the DeleteFromSearchListAnimation object.
/// </summary>
/// <param name="deleted" locid="WinJS.UI.Animation.createDeleteFromSearchListAnimation_p:deleted">
/// Single element or collection of elements which will be deleted.
/// When the animation completes, the application should hide the elements
/// or remove them from the document.
/// </param>
/// <param name="remaining" locid="WinJS.UI.Animation.createDeleteFromSearchListAnimation_p:remaining">
/// Single element or collection of elements whose positions were
/// affected by the deletion.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createDeleteFromSearchListAnimation_returnValue">
/// DeleteFromSearchListAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(DeleteFromSearchListAnimation, deleted, remaining);
},
showEdgeUI: function (element, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.showEdgeUI">
/// <summary locid="WinJS.UI.Animation.showEdgeUI">
/// Slides an element or elements into position at the edge of the screen.
/// This animation is designed for a small object like an appbar.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.showEdgeUI_p:element">
/// Single element or collection of elements to be slid into position.
/// The elements should be at their final positions
/// at the time the function is called.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.showEdgeUI_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the starting point of the animation.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.showEdgeUI_p:returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-showEdgeUI", [{ top: "-70px", left: "0px" }]);
return thisWinUI.executeAnimation(
element,
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: offsetArray.keyframe || translateCallback(offsetArray),
to: "none"
});
},
showPanel: function (element, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.showPanel">
/// <summary locid="WinJS.UI.Animation.showPanel">
/// Slides an element or elements into position at the edge of the screen.
/// This animation is designed for a large object like a keyboard.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.showPanel_p:element">
/// Single element or collection of elements to be slid into position.
/// The elements should be at their final positions
/// at the time the function is called.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.showPanel_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the starting point of the animation.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.showPanel_returnValue">
/// promise object
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-showPanel", [{ top: "0px", left: "364px", rtlflip: true }]);
return thisWinUI.executeAnimation(
element,
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 550,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: offsetArray.keyframe || translateCallback(offsetArray),
to: "none"
});
},
hideEdgeUI: function (element, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.hideEdgeUI">
/// <summary locid="WinJS.UI.Animation.hideEdgeUI">
/// Slides an element or elements at the edge of the screen out of view.
/// This animation is designed for a small object like an appbar.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.hideEdgeUI_p:element">
/// Single element or collection of elements to be slid out.
/// The elements should be at their onscreen positions
/// at the time the function is called.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.hideEdgeUI_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the ending point of the animation.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.hideEdgeUI_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-hideEdgeUI", [{ top: "-70px", left: "0px" }]);
return thisWinUI.executeAnimation(
element,
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: "none",
to: offsetArray.keyframe || translateCallback(offsetArray)
});
},
hidePanel: function (element, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.hidePanel">
/// <summary locid="WinJS.UI.Animation.hidePanel">
/// Slides an element or elements at the edge of the screen out of view.
/// This animation is designed for a large object like a keyboard.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.hidePanel_p:element">
/// Single element or collection of elements to be slid out.
/// The elements should be at their onscreen positions
/// at the time the function is called.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.hidePanel_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the ending point of the animation.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.hidePanel_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-hidePanel", [{ top: "0px", left: "364px", rtlflip: true }]);
return thisWinUI.executeAnimation(
element,
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 550,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: "none",
to: offsetArray.keyframe || translateCallback(offsetArray)
});
},
showPopup: function (element, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.showPopup">
/// <summary locid="WinJS.UI.Animation.showPopup">
/// Displays an element or elements in the style of a popup.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.showPopup_p:element">
/// Single element or collection of elements to be shown like a popup.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.showPopup_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the starting point of the animation.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.showPopup_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-showPopup", [{ top: "50px", left: "0px" }]);
return thisWinUI.executeAnimation(
element,
[{
keyframe: "WinJS-opacity-in",
property: "opacity",
delay: 83,
duration: 83,
timing: "linear",
from: 0,
to: 1
},
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: offsetArray.keyframe || translateCallback(offsetArray),
to: "none"
}]);
},
hidePopup: function (element) {
/// <signature helpKeyword="WinJS.UI.Animation.hidePopup">
/// <summary locid="WinJS.UI.Animation.hidePopup">
/// Removes a popup from the screen.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.hidePopup_p:element">
/// Single element or collection of elements to be hidden like a popup.
/// When the animation completes, the application should hide the elements
/// or remove them from the document.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.hidePopup_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
return thisWinUI.executeAnimation(
element,
{
keyframe: "WinJS-opacity-out",
property: "opacity",
delay: 0,
duration: 83,
timing: "linear",
from: 1,
to: 0
});
},
pointerDown: function (element) {
/// <signature helpKeyword="WinJS.UI.Animation.pointerDown">
/// <summary locid="WinJS.UI.Animation.pointerDown">
/// Execute a pointer-down animation.
/// Use the pointerUp animation to reverse the effect of this animation.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.pointerDown_p:element">
/// Single element or collection of elements responding to the
/// pointer-down event.
/// At the end of the animation, the elements' properties have been
/// modified to reflect the pointer-down state.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.pointerDown_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
return thisWinUI.executeTransition(
element,
{
property: mstransform,
delay: 0,
duration: 167,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: "scale(0.975, 0.975)"
});
},
pointerUp: function (element) {
/// <signature helpKeyword="WinJS.UI.Animation.pointerUp">
/// <summary locid="WinJS.UI.Animation.pointerUp">
/// Execute a pointer-up animation.
/// This reverses the effect of a pointerDown animation.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.pointerUp_p:element">
/// Single element or collection of elements responding to
/// the pointer-up event.
/// At the end of the animation, the elements' properties have been
/// returned to normal.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.pointerUp_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
return thisWinUI.executeTransition(
element,
{
property: mstransform,
delay: 0,
duration: 167,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
},
dragSourceStart: function (dragSource, affected) {
/// <signature helpKeyword="WinJS.UI.Animation.dragSourceStart">
/// <summary locid="WinJS.UI.Animation.dragSourceStart">
/// Execute a drag-start animation.
/// Use the dragSourceEnd animation to reverse the effects of this animation.
/// </summary>
/// <param name="dragSource" locid="WinJS.UI.Animation.dragSourceStart_p:dragSource">
/// Single element or collection of elements being dragged.
/// At the end of the animation, the elements' properties have been
/// modified to reflect the drag state.
/// </param>
/// <param name="affected" locid="WinJS.UI.Animation.dragSourceStart_p:affected">
/// Single element or collection of elements to highlight as not
/// being dragged.
/// At the end of the animation, the elements' properties have been
/// modified to reflect the drag state.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.dragSourceStart_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var promise1 = thisWinUI.executeTransition(
dragSource,
[{
property: mstransform,
delay: 0,
duration: 240,
timing : "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: "scale(1.05)"
},
{
property: "opacity",
delay: 0,
duration: 240,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: 0.65
}]);
var promise2 = thisWinUI.executeTransition(
affected,
{
property: mstransform,
delay: 0,
duration: 240,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: "scale(0.95)"
});
return WinJS.Promise.join([promise1, promise2]);
},
dragSourceEnd: function (dragSource, offset, affected) {
/// <signature helpKeyword="WinJS.UI.Animation.dragSourceEnd">
/// <summary locid="WinJS.UI.Animation.dragSourceEnd">
/// Execute a drag-end animation.
/// This reverses the effect of the dragSourceStart animation.
/// </summary>
/// <param name="dragSource" locid="WinJS.UI.Animation.dragSourceEnd_p:dragSource">
/// Single element or collection of elements no longer being dragged.
/// At the end of the animation, the elements' properties have been
/// returned to normal.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.dragSourceEnd_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the starting point of the animation.
/// If the number of offset objects is less than the length of the
/// dragSource parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <param name="affected" locid="WinJS.UI.Animation.dragSourceEnd_p:affected">
/// Single element or collection of elements which were highlighted as not
/// being dragged.
/// At the end of the animation, the elements' properties have been
/// returned to normal.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.dragSourceEnd_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-dragSourceEnd");
var promise1 = thisWinUI.executeTransition(
dragSource,
[{
property: mstransform,
delay: 0,
duration : 500,
timing : "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: "" // this removes the scale
},
{
property: "opacity",
delay: 0,
duration: 500,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: 1
}]);
var promise2 = thisWinUI.executeAnimation(
dragSource,
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 500,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: offsetArray.keyframe || translateCallback(offsetArray, "scale(1.05) "),
to: "none"
});
var promise3 = thisWinUI.executeTransition(
affected,
{
property: mstransform,
delay : 0,
duration : 500,
timing : "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
return WinJS.Promise.join([promise1, promise2, promise3]);
},
enterContent: function (incoming, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.enterContent">
/// <summary locid="WinJS.UI.Animation.enterContent">
/// Execute an enter-content animation.
/// </summary>
/// <param name="incoming" locid="WinJS.UI.Animation.enterContent_p:incoming">
/// Single element or collection of elements which represent
/// the incoming content.
/// At the end of the animation, the opacity of the elements is 1.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.enterContent_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the starting point of the animation.
/// If the number of offset objects is less than the length of the
/// incoming parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.enterContent_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray;
if (isSnapped()) {
offsetArray = new OffsetArray(offset, "WinJS-enterContent-snapped", [{ top: "0px", left: "20px", rtlflip: true }]);
} else {
offsetArray = new OffsetArray(offset, "WinJS-enterContent", [{ top: "0px", left: "40px", rtlflip: true }]);
}
var promise1 = thisWinUI.executeAnimation(
incoming,
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 550,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: offsetArray.keyframe || translateCallback(offsetArray),
to: "none"
});
var promise2 = thisWinUI.executeTransition(
incoming,
{
property: "opacity",
delay: 0,
duration: 170,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: 0,
to: 1
});
return WinJS.Promise.join([promise1, promise2]);
},
exitContent: function (outgoing, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.exitContent">
/// <summary locid="WinJS.UI.Animation.exitContent">
/// Execute an exit-content animation.
/// </summary>
/// <param name="outgoing" locid="WinJS.UI.Animation.exitContent_p:outgoing">
/// Single element or collection of elements which represent
/// the outgoing content.
/// At the end of the animation, the opacity of the elements is 0.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.exitContent_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the ending point of the animation.
/// If the number of offset objects is less than the length of the
/// outgoing parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.exitContent_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-exit", [{ top: "0px", left: "0px" }]);
var promise1 = thisWinUI.executeAnimation(
outgoing,
offset && {
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 117,
timing: "linear",
from: "none",
to: offsetArray.keyframe || translateCallback(offsetArray)
});
var promise2 = thisWinUI.executeTransition(
outgoing,
{
property: "opacity",
delay: 0,
duration: 117,
timing: "linear",
to: 0
});
return WinJS.Promise.join([promise1, promise2]);
},
dragBetweenEnter: function (target, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.dragBetweenEnter">
/// <summary locid="WinJS.UI.Animation.dragBetweenEnter">
/// Execute an animation which indicates that a dragged object
/// can be dropped between other elements.
/// Use the dragBetweenLeave animation to reverse the effects of this animation.
/// </summary>
/// <param name="target" locid="WinJS.UI.Animation.dragBetweenEnter_p:target">
/// Single element or collection of elements (usually two)
/// that the dragged object can be dropped between.
/// At the end of the animation, the elements' properties have been
/// modified to reflect the drag-between state.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.dragBetweenEnter_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the ending point of the animation.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.dragBetweenEnter_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, null, [{ top: "-40px", left: "0px" }, { top: "40px", left: "0px" }]);
return thisWinUI.executeTransition(
target,
{
property: mstransform,
delay: 0,
duration: 200,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: translateCallback(offsetArray, "scale(0.95) ")
});
},
dragBetweenLeave: function (target) {
/// <signature helpKeyword="WinJS.UI.Animation.dragBetweenLeave">
/// <summary locid="WinJS.UI.Animation.dragBetweenLeave">
/// Execute an animation which indicates that a dragged object
/// will no longer be dropped between other elements.
/// This reverses the effect of the dragBetweenEnter animation.
/// </summary>
/// <param name="target" locid="WinJS.UI.Animation.dragBetweenLeave_p:target">
/// Single element or collection of elements (usually two)
/// that the dragged object no longer will be dropped between.
/// At the end of the animation, the elements' properties have been
/// set to the dragSourceStart state.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.dragBetweenLeave_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
return thisWinUI.executeTransition(
target,
{
property: mstransform,
delay: 0,
duration: 200,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: "scale(0.95)"
});
},
swipeSelect: function (selected, selection) {
/// <signature helpKeyword="WinJS.UI.Animation.swipeSelect">
/// <summary locid="WinJS.UI.Animation.swipeSelect">
/// Slide a swipe-selected object back into position when the
/// pointer is released, and show the selection mark.
/// </summary>
/// <param name="selected" locid="WinJS.UI.Animation.swipeSelect_p:selected">
/// Single element or collection of elements being selected.
/// At the end of the animation, the elements' properties have been
/// returned to normal.
/// </param>
/// <param name="selection" locid="WinJS.UI.Animation.swipeSelect_p:selection">
/// Single element or collection of elements that is the selection mark.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.swipeSelect_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var promise1 = thisWinUI.executeTransition(
selected,
{
property: mstransform,
delay: 0,
duration: 300,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
var promise2 = thisWinUI.executeAnimation(
selection,
{
keyframe: "WinJS-opacity-in",
property: "opacity",
delay: 0,
duration: 300,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: 0,
to: 1
});
return WinJS.Promise.join([promise1, promise2]);
},
swipeDeselect: function (deselected, selection) {
/// <signature helpKeyword="WinJS.UI.Animation.swipeDeselect">
/// <summary locid="WinJS.UI.Animation.swipeDeselect">
/// Slide a swipe-deselected object back into position when the
/// pointer is released, and hide the selection mark.
/// </summary>
/// <param name="deselected" locid="WinJS.UI.Animation.swipeDeselect_p:deselected">
/// Single element or collection of elements being deselected.
/// At the end of the animation, the elements' properties have been
/// returned to normal.
/// </param>
/// <param name="selection" locid="WinJS.UI.Animation.swipeDeselect_p:selection">
/// Single element or collection of elements that is the selection mark.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.swipeDeselect_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var promise1 = thisWinUI.executeTransition(
deselected,
{
property: mstransform,
delay: 0,
duration: 300,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: ""
});
var promise2 = thisWinUI.executeAnimation(
selection,
{
keyframe: "WinJS-opacity-out",
property: "opacity",
delay: 0,
duration: 300,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: 1,
to: 0
});
return WinJS.Promise.join([promise1, promise2]);
},
swipeReveal: function (target, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.swipeReveal">
/// <summary locid="WinJS.UI.Animation.swipeReveal">
/// Reveal an object as the result of a swipe, or slide the
/// swipe-selected object back into position after the reveal.
/// </summary>
/// <param name="target" locid="WinJS.UI.Animation.swipeReveal_p:target">
/// Single element or collection of elements being selected.
/// At the end of the animation, the elements' properties have been
/// modified to reflect the specified offset.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.swipeReveal_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the ending point of the animation.
/// When moving the object back into position, the offset should be
/// { top: "0px", left: "0px" }.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// The default value describes the motion for a reveal.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.swipeReveal_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, null, [{ top: "25px", left: "0px" }]);
return thisWinUI.executeTransition(
target,
{
property: mstransform,
delay: 0,
duration: 300,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
to: translateCallback(offsetArray)
});
},
enterPage: function (element, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.enterPage">
/// <summary locid="WinJS.UI.Animation.enterPage">
/// Execute an enterPage animation.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.enterPage_p:element">
/// Single element or collection of elements representing the
/// incoming page.
/// At the end of the animation, the opacity of the elements is 1.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.enterPage_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the starting point of the animation.
/// If the number of offset objects is less than the length of the
/// element parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.enterPage_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray;
if (isSnapped()) {
offsetArray = new OffsetArray(offset, "WinJS-enterPage-snapped", [{ top: "0px", left: "40px", rtlflip: true }]);
} else {
offsetArray = new OffsetArray(offset, "WinJS-enterPage", [{ top: "0px", left: "100px", rtlflip: true }]);
}
var promise1 = thisWinUI.executeAnimation(
element,
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: staggerDelay(0, 83, 1, 333),
duration: 1000,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: offsetArray.keyframe || translateCallback(offsetArray),
to: "none"
});
var promise2 = thisWinUI.executeTransition(
element,
{
property: "opacity",
delay: staggerDelay(0, 83, 1, 333),
duration: 170,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: 0,
to: 1
});
return WinJS.Promise.join([promise1, promise2]);
},
exitPage: function (outgoing, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.exitPage">
/// <summary locid="WinJS.UI.Animation.exitPage">
/// Execute an exitPage animation.
/// </summary>
/// <param name="outgoing" locid="WinJS.UI.Animation.exitPage_p:outgoing">
/// Single element or collection of elements representing
/// the outgoing page.
/// At the end of the animation, the opacity of the elements is 0.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.exitPage_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the ending point of the animation.
/// If the number of offset objects is less than the length of the
/// outgoing parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.exitPage_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-exit", [{ top: "0px", left: "0px" }]);
var promise1 = thisWinUI.executeAnimation(
outgoing,
offset && {
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 117,
timing: "linear",
from: "none",
to: offsetArray.keyframe || translateCallback(offsetArray)
});
var promise2 = thisWinUI.executeTransition(
outgoing,
{
property: "opacity",
delay: 0,
duration: 117,
timing: "linear",
to: 0
});
return WinJS.Promise.join([promise1, promise2]);
},
crossFade: function (incoming, outgoing) {
/// <signature helpKeyword="WinJS.UI.Animation.crossFade">
/// <summary locid="WinJS.UI.Animation.crossFade">
/// Execute a crossFade animation.
/// </summary>
/// <param name="incoming" locid="WinJS.UI.Animation.crossFade_p:incoming">
/// Single incoming element or collection of incoming elements.
/// At the end of the animation, the opacity of the elements is 1.
/// </param>
/// <param name="outgoing" locid="WinJS.UI.Animation.crossFade_p:outgoing">
/// Single outgoing element or collection of outgoing elements.
/// At the end of the animation, the opacity of the elements is 0.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.crossFade_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var promise1 = thisWinUI.executeTransition(
incoming,
{
property: "opacity",
delay: 0,
duration: 167,
timing: "linear",
to: 1
});
var promise2 = thisWinUI.executeTransition(
outgoing,
{
property: "opacity",
delay: 0,
duration: 167,
timing: "linear",
to: 0
});
return WinJS.Promise.join([promise1, promise2]);
},
createPeekAnimation: function (element) {
/// <signature helpKeyword="WinJS.UI.Animation.createPeekAnimation">
/// <summary locid="WinJS.UI.Animation.createPeekAnimation">
/// Creates a peek animation.
/// After creating the PeekAnimation object,
/// modify the document to move the elements to their new positions,
/// then call the execute method on the PeekAnimation object.
/// </summary>
/// <param name="element" locid="WinJS.UI.Animation.createPeekAnimation_p:element">
/// Single element or collection of elements to be repositioned for peek.
/// </param>
/// <returns type="{ execute: Function }" locid="WinJS.UI.Animation.createPeekAnimation_returnValue">
/// PeekAnimation object whose execute method returns
/// a Promise that completes when the animation is complete.
/// </returns>
/// </signature>
return layoutTransition(PeekAnimation, null, element);
},
updateBadge: function (incoming, offset) {
/// <signature helpKeyword="WinJS.UI.Animation.updateBadge">
/// <summary locid="WinJS.UI.Animation.updateBadge">
/// Execute an updateBadge animation.
/// </summary>
/// <param name="incoming" locid="WinJS.UI.Animation.updateBadge_p:incoming">
/// Single element or collection of elements representing the
/// incoming badge.
/// </param>
/// <param name="offset" locid="WinJS.UI.Animation.updateBadge_p:offset">
/// Optional offset object or collection of offset objects
/// array describing the starting point of the animation.
/// If the number of offset objects is less than the length of the
/// incoming parameter, then the last value is repeated for all
/// remaining elements.
/// If this parameter is omitted, then a default value is used.
/// </param>
/// <returns type="WinJS.Promise" locid="WinJS.UI.Animation.updateBadge_returnValue">
/// Promise object that completes when the animation is complete.
/// </returns>
/// </signature>
var offsetArray = new OffsetArray(offset, "WinJS-updateBadge", [{ top: "24px", left: "0px" }]);
return thisWinUI.executeAnimation(
incoming,
[{
keyframe: "WinJS-opacity-in",
property: "opacity",
delay: 0,
duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: 0,
to: 1
},
{
keyframe: offsetArray.keyframe,
property: mstransform,
delay: 0,
duration: 1333,
timing: "cubic-bezier(0.1, 0.9, 0.2, 1)",
from: offsetArray.keyframe || translateCallback(offsetArray),
to: "none"
}]);
}
});
})(WinJS);
/*#DBG
var _ASSERT = function (condition) {
if (!condition) {
throw "ASSERT FAILED";
}
};
var _TRACE = function (text) {
if (window.console && console.log) {
console.log(text);
}
};
#DBG*/
// WinJS.Binding.ListDataSource
//
(function bindingListDataSourceInit(global, undefined) {
"use strict";
var errors = {
get noLongerMeaningful() { return WinJS.Promise.wrapError(new WinJS.ErrorFromName(WinJS.UI.EditError.noLongerMeaningful)); }
};
function findNextKey(list, index) {
var len = list.length;
while (index < len - 1) {
var item = list.getItem(++index);
if (item) {
return item.key;
}
}
return null;
}
function findPreviousKey(list, index) {
while (index > 0) {
var item = list.getItem(--index);
if (item) {
return item.key;
}
}
return null;
}
function subscribe(target, handlers) {
Object.keys(handlers).forEach(function (handler) {
target.addEventListener(handler, handlers[handler]);
});
}
function unsubscribe(target, handlers) {
Object.keys(handlers).forEach(function (handler) {
target.removeEventListener(handler, handlers[handler]);
});
}
var CompletePromise = WinJS.Promise.wrap().constructor;
var NullWrappedItem = WinJS.Class.derive(CompletePromise,
function () {
this._value = null;
}, {
release: function () { },
retain: function () { return this; }
}, {
/* empty */
}, {
supportedForProcessing: false,
}
);
var WrappedItem = WinJS.Class.derive(CompletePromise,
function (listBinding, item) {
this._value = item;
this._listBinding = listBinding;
}, {
handle: {
get: function () { return this._value.key; }
},
index: {
get: function () { return this._value.index; }
},
release: function () {
this._listBinding._release(this._value, this._listBinding._list.indexOfKey(this._value.key));
},
retain: function () {
this._listBinding._addRef(this._value, this._listBinding._list.indexOfKey(this._value.key));
return this;
}
}, {
/* empty */
}, {
supportedForProcessing: false,
}
);
var AsyncWrappedItem = WinJS.Class.derive(WinJS.Promise,
function (listBinding, item) {
var that = this;
this._item = item;
this._listBinding = listBinding;
WinJS.Promise.call(this, function (c) {
setImmediate(function () {
if (listBinding._released) {
that.cancel();
return;
}
c(item);
});
});
}, {
handle: {
get: function () { return this._item.key; }
},
index: {
get: function () { return this._item.index; }
},
release: function () {
this._listBinding._release(this._item, this._listBinding._list.indexOfKey(this._item.key));
},
retain: function () {
this._listBinding._addRef(this._item, this._listBinding._list.indexOfKey(this._item.key));
return this;
}
}, {
/* empty */
}, {
supportedForProcessing: false,
}
);
function wrap(listBinding, item) {
return item ? new WrappedItem(listBinding, item) : new NullWrappedItem();
}
function wrapAsync(listBinding, item) {
return item ? new AsyncWrappedItem(listBinding, item) : new NullWrappedItem();
}
function cloneWithIndex(list, item, index) {
return item && list._annotateWithIndex(item, index);
}
var ListBinding = WinJS.Class.define(
function ListBinding_ctor(dataSource, list, notificationHandler) {
this._dataSource = dataSource;
this._list = list;
this._editsCount = 0;
this._notificationHandler = notificationHandler;
this._pos = -1;
this._retained = [];
this._retained.length = list.length;
this._retainedKeys = {};
if (this._notificationHandler) {
var that = this;
this._handlers = {
itemchanged: function (event) {
var key = event.detail.key;
var index = event.detail.index;
var newItem = event.detail.newItem;
var oldItem = that._retained[index];
if (oldItem) {
if (oldItem.index !== index) {
var oldIndex = oldItem.index;
oldItem.index = index;
if (that._notificationHandler.indexChanged) {
that._notificationHandler.indexChanged(newItem.key, index, oldIndex);
}
}
newItem = cloneWithIndex(list, newItem, index);
newItem._retainedCount = oldItem._retainedCount;
that._retained[index] = newItem;
that._retainedKeys[key] = newItem;
that._beginEdits(list.length);
if (notificationHandler && notificationHandler.changed) {
notificationHandler.changed(
newItem,
oldItem
);
}
that._endEdits();
}
},
iteminserted: function (event) {
var key = event.detail.key;
var index = event.detail.index;
that._beginEdits(list.length - 1);
if (index <= that._pos) {
that._pos = Math.min(that._pos + 1, list.length);
}
var retained = that._retained;
// create a hole for this thing and then immediately make it undefined
retained.splice(index, 0, 0);
delete retained[index];
if (that._shouldNotify(index) || list.length === 1) {
if (notificationHandler && notificationHandler.inserted) {
notificationHandler.inserted(
wrap(that, cloneWithIndex(list, list.getItem(index), index)),
findPreviousKey(list, index),
findNextKey(list, index)
);
}
}
that._endEdits();
},
itemmoved: function (event) {
var key = event.detail.key;
var oldIndex = event.detail.oldIndex;
var newIndex = event.detail.newIndex;
that._beginEdits(list.length);
if (oldIndex < that._pos || newIndex <= that._pos) {
if (newIndex > that._pos) {
that._pos = Math.max(-1, that._pos - 1);
} else if (oldIndex > that._pos) {
that._pos = Math.min(that._pos + 1, list.length);
}
}
var retained = that._retained;
var item = retained.splice(oldIndex, 1)[0];
retained.splice(newIndex, 0, item);
if (!item) {
delete retained[newIndex];
item = cloneWithIndex(list, list.getItem(newIndex), newIndex);
}
item._moved = true;
that._addRef(item, newIndex);
that._endEdits();
},
itemremoved: function (event) {
var key = event.detail.key;
var index = event.detail.index;
that._beginEdits(list.length + 1);
if (index < that._pos) {
that._pos = Math.max(-1, that._pos - 1);
}
var retained = that._retained;
var retainedKeys = that._retainedKeys;
var wasRetained = index in retained;
retained.splice(index, 1);
delete retainedKeys[key];
if (wasRetained && notificationHandler && notificationHandler.removed) {
notificationHandler.removed(key, false);
}
that._endEdits();
},
reload: function () {
that._retained = [];
that._retainedKeys = {};
if (notificationHandler && notificationHandler.reload) {
notificationHandler.reload();
}
}
};
subscribe(this._list, this._handlers);
}
}, {
_addRef: function (item, index) {
if (index in this._retained) {
this._retained[index]._retainedCount++;
} else {
this._retained[index] = item;
this._retainedKeys[item.key] = item;
item._retainedCount = 1;
}
},
_release: function (item, index) {
var retained = this._retained[index];
if (retained) {
//#DBG _ASSERT(retained.key === item.key);
if (retained._retainedCount === 1) {
delete this._retained[index];
delete this._retainedKeys[retained.key];
} else {
retained._retainedCount--;
}
}
/*#DBG
// If an item isn't found in the retained map, it was either removed from retainedCount reaching zero, or removed from the map by a removed notification.
// We'll decrement the count here for debugging purposes. If retainedCount is less than zero, there's a refcounting error somewhere.
if (!retained) {
item._retainedCount--;
_ASSERT(item._retainedCount >= 0);
}
#DBG*/
},
_shouldNotify: function (index) {
var retained = this._retained;
return index in retained || index + 1 in retained || index - 1 in retained;
},
_notifyCountChanged: function () {
var oldCount = this._countAtBeginEdits;
var newCount = this._list.length;
if (oldCount !== newCount) {
if (this._notificationHandler && this._notificationHandler.countChanged) {
this._notificationHandler.countChanged(newCount, oldCount);
}
}
},
_notifyIndicesChanged: function () {
var retained = this._retained;
for (var i = 0, len = retained.length; i < len; i++) {
var item = retained[i];
if (item && item.index !== i) {
var newIndex = i;
var oldIndex = item.index;
item.index = newIndex;
if (this._notificationHandler && this._notificationHandler.indexChanged) {
this._notificationHandler.indexChanged(item.key, newIndex, oldIndex);
}
}
}
},
_notifyMoved: function () {
var retained = this._retained;
for (var i = 0, len = retained.length; i < len; i++) {
var item = retained[i];
if (item && item._moved) {
item._moved = false;
this._release(item, i);
if (this._shouldNotify(i)) {
if (this._notificationHandler && this._notificationHandler.moved) {
this._notificationHandler.moved(
wrap(this, item),
findPreviousKey(this._list, i),
findNextKey(this._list, i)
);
}
}
}
}
},
_beginEdits: function (length, explicit) {
this._editsCount++;
if (this._editsCount === 1 && this._notificationHandler) {
if (!explicit) {
// Batch all edits between now and the setImmediate firing. This has the effect
// of batching synchronous edits.
//
this._editsCount++;
var that = this;
setImmediate(function () {
that._endEdits();
});
}
if (this._notificationHandler && this._notificationHandler.beginNotifications) {
this._notificationHandler.beginNotifications();
}
this._countAtBeginEdits = length;
}
},
_endEdits: function () {
this._editsCount--;
if (this._editsCount === 0 && this._notificationHandler) {
this._notifyIndicesChanged();
this._notifyMoved();
this._notifyCountChanged();
if (this._notificationHandler && this._notificationHandler.endNotifications) {
this._notificationHandler.endNotifications();
}
}
},
jumpToItem: function (item) {
var index = this._list.indexOfKey(item.handle);
if (index === -1) {
return WinJS.Promise.wrap(null);
}
this._pos = index;
return this.current();
},
current: function () {
return this.fromIndex(this._pos);
},
previous: function () {
this._pos = Math.max(-1, this._pos - 1);
return this._fromIndex(this._pos, true);
},
next: function () {
this._pos = Math.min(this._pos + 1, this._list.length);
return this._fromIndex(this._pos, true);
},
releaseItem: function (item) {
if (item.release) {
item.release();
} else {
this._release(item, this._list.indexOfKey(item.key));
}
},
release: function () {
if (this._notificationHandler) {
unsubscribe(this._list, this._handlers);
}
this._notificationHandler = null;
this._dataSource._releaseBinding(this);
this._released = true;
},
first: function () {
return this.fromIndex(0);
},
last: function () {
return this.fromIndex(this._list.length - 1);
},
fromKey: function (key) {
var retainedKeys = this._retainedKeys;
var item;
if (key in retainedKeys) {
item = retainedKeys[key];
} else {
item = cloneWithIndex(this._list, this._list.getItemFromKey(key), this._list.indexOfKey(key));
}
return wrap(this, item);
},
fromIndex: function (index) {
return this._fromIndex(index, false);
},
_fromIndex: function (index, async) {
var retained = this._retained;
var item;
if (index in retained) {
item = retained[index];
} else {
item = cloneWithIndex(this._list, this._list.getItem(index), index);
}
return async ? wrapAsync(this, item) : wrap(this, item);
},
}, {
supportedForProcessing: false,
}
);
function insertAtStart(unused, data) {
// List ignores the key because its key management is internal
this._list.unshift(data);
return this.itemFromIndex(0);
}
function insertBefore(unused, data, nextKey) {
// List ignores the key because its key management is internal
var index = this._list.indexOfKey(nextKey);
if (index === -1) {
return errors.noLongerMeaningful;
}
this._list.splice(index, 0, data);
return this.itemFromIndex(index);
}
function insertAfter(unused, data, previousKey) {
// List ignores the key because its key management is internal
var index = this._list.indexOfKey(previousKey);
if (index === -1) {
return errors.noLongerMeaningful;
}
index += 1;
this._list.splice(index, 0, data);
return this.itemFromIndex(index);
}
function insertAtEnd(unused, data) {
// List ignores the key because its key management is internal
this._list.push(data);
return this.itemFromIndex(this._list.length - 1);
}
function change(key, newData) {
var index = this._list.indexOfKey(key);
if (index === -1) {
return errors.noLongerMeaningful;
}
this._list.setAt(index, newData);
return this.itemFromIndex(index);
}
function moveToStart(key) {
var sourceIndex = this._list.indexOfKey(key);
if (sourceIndex === -1) {
return errors.noLongerMeaningful;
}
var targetIndex = 0;
this._list.move(sourceIndex, targetIndex);
return this.itemFromIndex(targetIndex);
}
function moveBefore(key, nextKey) {
var sourceIndex = this._list.indexOfKey(key);
var targetIndex = this._list.indexOfKey(nextKey);
if (sourceIndex === -1 || targetIndex === -1) {
return errors.noLongerMeaningful;
}
targetIndex = sourceIndex < targetIndex ? targetIndex - 1 : targetIndex;
this._list.move(sourceIndex, targetIndex);
return this.itemFromIndex(targetIndex);
}
function moveAfter(key, previousKey) {
var sourceIndex = this._list.indexOfKey(key);
var targetIndex = this._list.indexOfKey(previousKey);
if (sourceIndex === -1 || targetIndex === -1) {
return errors.noLongerMeaningful;
}
targetIndex = sourceIndex <= targetIndex ? targetIndex : targetIndex + 1;
this._list.move(sourceIndex, targetIndex);
return this.itemFromIndex(targetIndex);
}
function moveToEnd(key) {
var sourceIndex = this._list.indexOfKey(key);
if (sourceIndex === -1) {
return errors.noLongerMeaningful;
}
var targetIndex = this._list.length - 1;
this._list.move(sourceIndex, targetIndex);
return this.itemFromIndex(targetIndex);
}
function remove(key) {
var index = this._list.indexOfKey(key);
if (index === -1) {
return errors.noLongerMeaningful;
}
this._list.splice(index, 1);
return WinJS.Promise.wrap();
}
var bindingId = 0;
var DataSource = WinJS.Class.define(
function DataSource_ctor(list) {
this._bindings = [];
this._list = list;
if (list.unshift) {
this.insertAtStart = insertAtStart;
}
if (list.push) {
this.insertAtEnd = insertAtEnd;
}
if (list.setAt) {
this.change = change;
}
if (list.splice) {
this.insertAfter = insertAfter;
this.insertBefore = insertBefore;
this.remove = remove;
}
if (list.move) {
this.moveAfter = moveAfter;
this.moveBefore = moveBefore;
this.moveToEnd = moveToEnd;
this.moveToStart = moveToStart;
}
}, {
_releaseBinding: function (binding) {
delete this._bindings[binding._id];
},
addEventListener: function () {
// nop, we don't send statusChanged
},
removeEventListener: function () {
// nop, we don't send statusChanged
},
createListBinding: function (notificationHandler) {
var binding = new ListBinding(this, this._list, notificationHandler);
binding._id = (++bindingId);
this._bindings[binding._id] = binding;
return binding;
},
getCount: function () {
return WinJS.Promise.wrap(this._list.length);
},
itemFromKey: function (key) {
// Clone with a dummy index
var list = this._list,
item = cloneWithIndex(list, list.getItemFromKey(key), -1);
// Override the index property with a getter
Object.defineProperty(item, "index", {
get: function () {
return list.indexOfKey(key);
},
enumerable: false,
configurable: true
});
return WinJS.Promise.wrap(item);
},
itemFromIndex: function (index) {
return WinJS.Promise.wrap(cloneWithIndex(this._list, this._list.getItem(index), index));
},
list: {
get: function () { return this._list; }
},
beginEdits: function () {
var length = this._list.length;
this._bindings.forEach(function (binding) {
binding._beginEdits(length, true);
});
},
endEdits: function () {
this._bindings.forEach(function (binding) {
binding._endEdits();
});
},
invalidateAll: function() {
return WinJS.Promise.wrap();
},
//
// insert* and change are not implemented as I don't understand how they are
// used by the controls since it is hard to fathom how they would be able
// to make up unique keys. Manual editing of the List is meant to go through
// the list itself.
//
// move* are implemented only if the underlying list supports move(). The
// GroupsListProjection for instance does not.
//
moveAfter: undefined,
moveBefore: undefined,
moveToEnd: undefined,
moveToStart: undefined
}, {
supportedForProcessing: false,
}
);
if (WinJS.Binding && WinJS.Binding.List) {
Object.defineProperty(Object.getPrototypeOf(Object.getPrototypeOf(WinJS.Binding.List.prototype)), "dataSource", {
get: function () { return (this._dataSource = this._dataSource || new DataSource(this)); }
});
}
}(this));
// Virtualized Data Source
(function listDataSourceInit(undefined) {
"use strict";
var Promise = WinJS.Promise,
Signal = WinJS._Signal,
UI = WinJS.UI;
// Private statics
var strings = {
get listDataAdapterIsInvalid() { return WinJS.Resources._getWinJSString("ui/listDataAdapterIsInvalid").value; },
get indexIsInvalid() { return WinJS.Resources._getWinJSString("ui/indexIsInvalid").value; },
get keyIsInvalid() { return WinJS.Resources._getWinJSString("ui/keyIsInvalid").value; },
get invalidItemReturned() { return WinJS.Resources._getWinJSString("ui/undefinedItemReturned").value; },
get invalidKeyReturned() { return WinJS.Resources._getWinJSString("ui/invalidKeyReturned").value; },
get invalidIndexReturned() { return WinJS.Resources._getWinJSString("ui/invalidIndexReturned").value; },
get invalidCountReturned() { return WinJS.Resources._getWinJSString("ui/invalidCountReturned").value; },
get invalidRequestedCountReturned() { return WinJS.Resources._getWinJSString("ui/invalidRequestedCountReturned").value; },
};
var statusChangedEvent = "statuschanged";
function _baseDataSourceConstructor(listDataAdapter, options) {
/// <signature helpKeyword="WinJS.UI.VirtualizedDataSource._baseDataSourceConstructor">
/// <summary locid="WinJS.UI.VirtualizedDataSource._baseDataSourceConstructor">
/// Initializes the VirtualizedDataSource base class of a custom data source.
/// </summary>
/// <param name="listDataAdapter" type="IListDataAdapter" locid="WinJS.UI.VirtualizedDataSource._baseDataSourceConstructor_p:itemIndex">
/// An object that implements IListDataAdapter and supplies data to the VirtualizedDataSource.
/// </param>
/// <param name="options" optional="true" type="Object" locid="WinJS.UI.VirtualizedDataSource._baseDataSourceConstructor_p:options">
/// An object that contains properties that specify additonal options for the VirtualizedDataSource:
///
/// cacheSize
/// A Number that specifies minimum number of unrequested items to cache in case they are requested.
///
/// The options parameter is optional.
/// </param>
/// </signature>
// Private members
var listDataNotificationHandler,
cacheSize,
status,
statusPending,
statusChangePosted,
bindingMap,
nextListBindingID,
nextHandle,
nextListenerID,
getCountPromise,
resultsProcessed,
beginEditsCalled,
editsInProgress,
firstEditInProgress,
editQueue,
editsQueued,
synchronousEdit,
waitForRefresh,
dataNotificationsInProgress,
countDelta,
indexUpdateDeferred,
nextTempKey,
currentRefreshID,
fetchesPosted,
nextFetchID,
fetchesInProgress,
fetchCompleteCallbacks,
startMarker,
endMarker,
knownCount,
slotsStart,
slotListEnd,
slotsEnd,
handleMap,
keyMap,
indexMap,
releasedSlots,
lastSlotReleased,
reduceReleasedSlotCountPosted,
refreshRequested,
refreshInProgress,
refreshSignal,
refreshFetchesInProgress,
refreshItemsFetched,
refreshCount,
refreshStart,
refreshEnd,
keyFetchIDs,
refreshKeyMap,
refreshIndexMap,
deletedKeys,
synchronousProgress,
reentrantContinue,
synchronousRefresh,
reentrantRefresh;
/*#DBG
var totalSlots = 0;
function VERIFYLIST() {
_ASSERT(slotListEnd.lastInSequence);
_ASSERT(slotsEnd.firstInSequence);
checkListIntegrity(slotsStart, slotsEnd, keyMap, indexMap);
}
function VERIFYREFRESHLIST() {
checkListIntegrity(refreshStart, refreshEnd, refreshKeyMap, refreshIndexMap);
}
function checkListIntegrity(listStart, listEnd, keyMapForSlot, indexMapForSlot) {
if (UI.VirtualizedDataSource._internalValidation) {
var listEndReached,
slotWithoutIndexReached;
for (var slotCheck = listStart; slotCheck !== listEnd; slotCheck = slotCheck.next) {
_ASSERT(slotCheck.next);
_ASSERT(slotCheck.next.prev === slotCheck);
if (slotCheck.lastInSequence) {
_ASSERT(slotCheck.next.firstInSequence);
}
if (slotCheck !== listStart) {
_ASSERT(slotCheck.prev);
_ASSERT(slotCheck.prev.next === slotCheck);
if (slotCheck.firstInSequence) {
_ASSERT(slotCheck.prev.lastInSequence);
}
}
if (slotCheck.item || slotCheck.itemNew) {
_ASSERT(editsQueued || slotCheck.key);
}
if (slotCheck.key) {
_ASSERT(keyMapForSlot[slotCheck.key] === slotCheck);
if (slotCheck.item) {
_ASSERT(slotCheck.item.key === slotCheck.key);
}
}
if (typeof slotCheck.index === "number") {
_ASSERT(!listEndReached);
if (!indexUpdateDeferred) {
_ASSERT(indexMapForSlot[slotCheck.index] === slotCheck);
_ASSERT(slotCheck === listStart || slotCheck.prev.index < slotCheck.index);
_ASSERT(!slotCheck.firstInSequence || !slotCheck.prev || slotCheck.prev.index !== slotCheck.index - 1);
_ASSERT(!slotCheck.lastInSequence || !slotCheck.next || slotCheck.next.index !== slotCheck.index + 1);
if (slotCheck.item) {
_ASSERT(listStart === refreshStart || slotCheck.item.index === slotCheck.index);
}
}
} else {
slotWithoutIndexReached = true;
}
if (slotCheck === slotListEnd) {
listEndReached = true;
}
if (slotCheck.lastInSequence && !listEndReached && !indexUpdateDeferred) {
_ASSERT(!slotWithoutIndexReached);
}
}
}
}
function OUTPUTLIST() {
outputList("Main List", slotsStart);
}
function OUTPUTREFRESHLIST() {
outputList("Refresh List", refreshStart);
}
function outputList(header, slotFirst) {
_TRACE("-- " + header + " --");
for (var slot = slotFirst; slot; slot = slot.next) {
var line = (slot.firstInSequence ? "[" : " ");
if (slot.index !== undefined && slot !== slotsStart && slot !== refreshStart) {
line += slot.index + ": ";
}
if (slot === slotsStart || slot === refreshStart) {
line += "{";
} else if (slot === slotListEnd || slot === refreshEnd) {
line += "}";
} else if (slot === slotsEnd) {
line += "-";
} else {
line += (slot.key ? '"' + slot.key + '"' : "?");
}
if (slot.bindingMap) {
line += " (";
var first = true;
for (var listBindingID in slot.bindingMap) {
if (first) {
first = false;
} else {
line += ", ";
}
line += listBindingID;
}
line += ")";
}
if (slot.itemNew) {
line += " itemNew";
}
if (slot.item) {
line += " item";
}
if (slot.fetchListeners) {
line += " fetching";
}
if (slot.directFetchListeners) {
line += " directFetching";
}
if (slot.indexRequested) {
line += " index";
}
if (slot.keyRequested) {
line += " key";
}
if (slot.description) {
line += " description=" + JSON.stringify(slot.description);
}
if (slotFetchInProgress(slot)) {
line += " now";
}
if (typeof slot.handle === "string") {
line += " <" + slot.handle + ">";
}
if (slot.lastInSequence) {
line += "]";
}
_TRACE(line);
}
}
#DBG*/
function isNonNegativeNumber(n) {
return (typeof n === "number") && n >= 0;
}
function isNonNegativeInteger(n) {
return isNonNegativeNumber(n) && n === Math.floor(n);
}
function validateIndexReturned(index) {
// Ensure that index is always undefined or a non-negative integer
if (index === null) {
index = undefined;
} else if (index !== undefined && !isNonNegativeInteger(index)) {
throw new WinJS.ErrorFromName("WinJS.UI.ListDataSource.InvalidIndexReturned", strings.invalidIndexReturned);
}
return index;
}
function validateCountReturned(count) {
// Ensure that count is always undefined or a non-negative integer
if (count === null) {
count = undefined;
} else if (count !== undefined && !isNonNegativeInteger(count) && count !== CountResult.unknown) {
throw new WinJS.ErrorFromName("WinJS.UI.ListDataSource.InvalidCountReturned", strings.invalidCountReturned);
}
return count;
}
// Slot List
function createSlot() {
var handle = (nextHandle++).toString(),
slotNew = {
handle: handle,
item: null,
itemNew: null,
fetchListeners: null,
cursorCount: 0,
bindingMap: null
};
// Deliberately not initialized:
// - directFetchListeners
handleMap[handle] = slotNew;
return slotNew;
}
function createPrimarySlot() {
/*#DBG
totalSlots++;
#DBG*/
return createSlot();
}
function insertSlot(slot, slotNext) {
//#DBG _ASSERT(slotNext);
//#DBG _ASSERT(slotNext.prev);
slot.prev = slotNext.prev;
slot.next = slotNext;
slot.prev.next = slot;
slotNext.prev = slot;
}
function removeSlot(slot) {
//#DBG _ASSERT(slot.prev.next === slot);
//#DBG _ASSERT(slot.next.prev === slot);
//#DBG _ASSERT(slot !== slotsStart && slot !== slotListEnd && slot !== slotsEnd);
if (slot.lastInSequence) {
delete slot.lastInSequence;
slot.prev.lastInSequence = true;
}
if (slot.firstInSequence) {
delete slot.firstInSequence;
slot.next.firstInSequence = true;
}
slot.prev.next = slot.next;
slot.next.prev = slot.prev;
}
function sequenceStart(slot) {
while (!slot.firstInSequence) {
slot = slot.prev;
}
return slot;
}
function sequenceEnd(slot) {
while (!slot.lastInSequence) {
slot = slot.next;
}
return slot;
}
// Does a little careful surgery to the slot sequence from slotFirst to slotLast before slotNext
function moveSequenceBefore(slotNext, slotFirst, slotLast) {
//#DBG _ASSERT(slotFirst !== slotsStart && slotLast !== slotListEnd && slotLast !== slotsEnd);
//#DBG _ASSERT(slotFirst.firstInSequence && slotLast.lastInSequence);
//#DBG _ASSERT(slotNext.firstInSequence && slotNext.prev.lastInSequence);
slotFirst.prev.next = slotLast.next;
slotLast.next.prev = slotFirst.prev;
slotFirst.prev = slotNext.prev;
slotLast.next = slotNext;
slotFirst.prev.next = slotFirst;
slotNext.prev = slotLast;
return true;
}
// Does a little careful surgery to the slot sequence from slotFirst to slotLast after slotPrev
function moveSequenceAfter(slotPrev, slotFirst, slotLast) {
//#DBG _ASSERT(slotFirst !== slotsStart && slotLast !== slotsEnd);
//#DBG _ASSERT(slotFirst.firstInSequence && slotLast.lastInSequence);
//#DBG _ASSERT(slotPrev.lastInSequence && slotPrev.next.firstInSequence);
slotFirst.prev.next = slotLast.next;
slotLast.next.prev = slotFirst.prev;
slotFirst.prev = slotPrev;
slotLast.next = slotPrev.next;
slotPrev.next = slotFirst;
slotLast.next.prev = slotLast;
return true;
}
function mergeSequences(slotPrev) {
delete slotPrev.lastInSequence;
delete slotPrev.next.firstInSequence;
}
function splitSequence(slotPrev) {
var slotNext = slotPrev.next;
slotPrev.lastInSequence = true;
slotNext.firstInSequence = true;
if (slotNext === slotListEnd) {
// Clear slotListEnd's index, as that's now unknown
changeSlotIndex(slotListEnd, undefined);
}
}
// Inserts a slot in the middle of a sequence or between sequences. If the latter, mergeWithPrev and mergeWithNext
// parameters specify whether to merge the slot with the previous sequence, or next, or neither.
function insertAndMergeSlot(slot, slotNext, mergeWithPrev, mergeWithNext) {
insertSlot(slot, slotNext);
var slotPrev = slot.prev;
if (slotPrev.lastInSequence) {
//#DBG _ASSERT(slotNext.firstInSequence);
if (mergeWithPrev) {
delete slotPrev.lastInSequence;
} else {
slot.firstInSequence = true;
}
if (mergeWithNext) {
delete slotNext.firstInSequence;
} else {
slot.lastInSequence = true;
}
}
}
// Keys and Indices
function setSlotKey(slot, key) {
//#DBG _ASSERT(!slot.key);
//#DBG _ASSERT(!keyMap[key]);
slot.key = key;
// Add the slot to the keyMap, so it is possible to quickly find the slot given its key
keyMap[slot.key] = slot;
}
function setSlotIndex(slot, index, indexMapForSlot) {
// Tolerate NaN, so clients can pass (undefined - 1) or (undefined + 1)
if (+index === index) {
//#DBG _ASSERT(indexUpdateDeferred || !indexMapForSlot[index]);
slot.index = index;
// Add the slot to the indexMap, so it is possible to quickly find the slot given its index
indexMapForSlot[index] = slot;
if (!indexUpdateDeferred) {
// See if any sequences should be merged
if (slot.firstInSequence && slot.prev && slot.prev.index === index - 1) {
mergeSequences(slot.prev);
}
if (slot.lastInSequence && slot.next && slot.next.index === index + 1) {
mergeSequences(slot);
}
}
}
}
// Creates a new slot and adds it to the slot list before slotNext
function createAndAddSlot(slotNext, indexMapForSlot) {
var slotNew = (indexMapForSlot === indexMap ? createPrimarySlot() : createSlot());
insertSlot(slotNew, slotNext);
return slotNew;
}
function createSlotSequence(slotNext, index, indexMapForSlot) {
//#DBG _ASSERT(slotNext.prev.lastInSequence);
//#DBG _ASSERT(slotNext.firstInSequence);
var slotNew = createAndAddSlot(slotNext, indexMapForSlot);
slotNew.firstInSequence = true;
slotNew.lastInSequence = true;
setSlotIndex(slotNew, index, indexMapForSlot);
return slotNew;
}
function createPrimarySlotSequence(slotNext, index) {
return createSlotSequence(slotNext, index, indexMap);
}
function addSlotBefore(slotNext, indexMapForSlot) {
//#DBG _ASSERT(slotNext.firstInSequence);
//#DBG _ASSERT(slotNext.prev.lastInSequence);
var slotNew = createAndAddSlot(slotNext, indexMapForSlot);
delete slotNext.firstInSequence;
// See if we've bumped into the previous sequence
if (slotNew.prev.index === slotNew.index - 1) {
delete slotNew.prev.lastInSequence;
} else {
slotNew.firstInSequence = true;
}
setSlotIndex(slotNew, slotNext.index - 1, indexMapForSlot);
return slotNew;
}
function addSlotAfter(slotPrev, indexMapForSlot) {
//#DBG _ASSERT(slotPrev !== slotListEnd);
//#DBG _ASSERT(slotPrev.lastInSequence);
//#DBG _ASSERT(slotPrev.next.firstInSequence);
var slotNew = createAndAddSlot(slotPrev.next, indexMapForSlot);
delete slotPrev.lastInSequence;
// See if we've bumped into the next sequence
if (slotNew.next.index === slotNew.index + 1) {
delete slotNew.next.firstInSequence;
} else {
slotNew.lastInSequence = true;
}
setSlotIndex(slotNew, slotPrev.index + 1, indexMapForSlot);
return slotNew;
}
function reinsertSlot(slot, slotNext, mergeWithPrev, mergeWithNext) {
insertAndMergeSlot(slot, slotNext, mergeWithPrev, mergeWithNext);
//#DBG _ASSERT(!keyMap[slot.key]);
keyMap[slot.key] = slot;
var index = slot.index;
if (slot.index !== undefined) {
indexMap[slot.index] = slot;
}
}
function removeSlotPermanently(slot) {
/*#DBG
_ASSERT(totalSlots > 0);
totalSlots--;
#DBG*/
removeSlot(slot);
if (slot.key) {
delete keyMap[slot.key];
}
if (slot.index !== undefined && indexMap[slot.index] === slot) {
delete indexMap[slot.index];
}
var bindingMap = slot.bindingMap;
for (var listBindingID in bindingMap) {
var handle = bindingMap[listBindingID].handle;
if (handle && handleMap[handle] === slot) {
delete handleMap[handle];
}
}
// Invalidating the slot's handle marks it as deleted
if (handleMap[slot.handle] === slot) {
delete handleMap[slot.handle];
}
}
function slotPermanentlyRemoved(slot) {
return !handleMap[slot.handle];
}
function successorFromIndex(index, indexMapForSlot, listStart, listEnd, skipPreviousIndex) {
//#DBG _ASSERT(index !== undefined);
// Try the previous index
var slotNext = (skipPreviousIndex ? null : indexMapForSlot[index - 1]);
if (slotNext && (slotNext.next !== listEnd || listEnd.firstInSequence)) {
// We want the successor
slotNext = slotNext.next;
} else {
// Try the next index
slotNext = indexMapForSlot[index + 1];
if (!slotNext) {
// Resort to a linear search
slotNext = listStart.next;
var lastSequenceStart;
while (true) {
//#DBG _ASSERT(slotNext);
//#DBG _ASSERT(slotNext.index !== index);
if (slotNext.firstInSequence) {
lastSequenceStart = slotNext;
}
if (!(index >= slotNext.index) || slotNext === listEnd) {
break;
}
slotNext = slotNext.next;
}
if (slotNext === listEnd && !listEnd.firstInSequence) {
// Return the last insertion point between sequences, or undefined if none
slotNext = (lastSequenceStart && lastSequenceStart.index === undefined ? lastSequenceStart : undefined);
}
}
}
return slotNext;
}
// Slot Items
function isPlaceholder(slot) {
//#DBG _ASSERT(slot !== slotsStart && slot !== slotsEnd);
return !slot.item && !slot.itemNew && slot !== slotListEnd;
}
function defineHandleProperty(item, handle) {
Object.defineProperty(item, "handle", {
value: handle,
writable: false,
enumerable: false,
configurable: true
});
}
function defineCommonItemProperties(item, slot, handle) {
defineHandleProperty(item, handle);
Object.defineProperty(item, "index", {
get: function () {
while (slot.slotMergedWith) {
slot = slot.slotMergedWith;
}
return slot.index;
},
enumerable: false,
configurable: true
});
}
function validateData(data) {
if (data === undefined) {
return data;
} else {
// Convert the data object to JSON to enforce the constraints we want. For example, we don't want
// functions, arrays with extra properties, DOM objects, cyclic or acyclic graphs, or undefined values.
var dataValidated = JSON.stringify(data);
if (dataValidated === undefined) {
throw new WinJS.ErrorFromName("WinJS.UI.ListDataSource.ObjectIsNotValidJson", strings.objectIsNotValidJson);
}
return dataValidated;
}
}
function itemSignature(item) {
return (
listDataAdapter.itemSignature ?
listDataAdapter.itemSignature(item.data) :
validateData(item.data)
);
}
function prepareSlotItem(slot) {
var item = slot.itemNew;
slot.itemNew = null;
if (item) {
defineCommonItemProperties(item, slot, slot.handle);
if (!listDataAdapter.compareByIdentity) {
// Store the item signature or a stringified copy of the data for comparison later
slot.signature = itemSignature(item);
}
}
slot.item = item;
delete slot.indexRequested;
delete slot.keyRequested;
}
// Slot Caching
function slotRetained(slot) {
return slot.bindingMap || slot.cursorCount > 0;
}
function slotRequested(slot) {
return slotRetained(slot) || slot.fetchListeners || slot.directFetchListeners;
}
function slotLive(slot) {
return slotRequested(slot) || (!slot.firstInSequence && slotRetained(slot.prev)) || (!slot.lastInSequence && slotRetained(slot.next)) ||
(!listDataAdapter.itemsFromIndex && (
(!slot.firstInSequence && slot.prev !== slotsStart && !(slot.prev.item || slot.prev.itemNew)) |
(!slot.lastInSequence && slot.next !== slotListEnd && !(slot.next.item || slot.next.itemNew))
));
}
function deleteUnnecessarySlot(slot) {
splitSequence(slot);
removeSlotPermanently(slot);
}
function reduceReleasedSlotCount() {
// Must not release slots while edits are queued, as undo queue might refer to them
if (!editsQueued) {
// If lastSlotReleased is no longer valid, use the end of the list instead
if (!lastSlotReleased || slotPermanentlyRemoved(lastSlotReleased)) {
lastSlotReleased = slotListEnd.prev;
}
// Now use the simple heuristic of walking outwards in both directions from lastSlotReleased until the
// desired cache size is reached, then removing everything else.
var slotPrev = lastSlotReleased.prev,
slotNext = lastSlotReleased.next,
releasedSlotsFound = 0;
var considerDeletingSlot = function (slotToDelete) {
if (slotToDelete !== slotListEnd && !slotLive(slotToDelete)) {
if (releasedSlotsFound <= cacheSize) {
releasedSlotsFound++;
} else {
deleteUnnecessarySlot(slotToDelete);
}
}
}
while (slotPrev || slotNext) {
if (slotPrev) {
var slotPrevToDelete = slotPrev;
slotPrev = slotPrevToDelete.prev;
if (slotPrevToDelete !== slotsStart) {
considerDeletingSlot(slotPrevToDelete);
}
}
if (slotNext) {
var slotNextToDelete = slotNext;
slotNext = slotNextToDelete.next;
if (slotNextToDelete !== slotsEnd) {
considerDeletingSlot(slotNextToDelete);
}
}
}
// Reset the count to zero, so this method is only called periodically
releasedSlots = 0;
}
}
function releaseSlotIfUnrequested(slot) {
if (!slotRequested(slot)) {
if (UI._PerfMeasurement_leakSlots) {
return;
}
releasedSlots++;
// Must not release slots while edits are queued, as undo queue might refer to them. If a refresh is in
// progress, retain all slots, just in case the user re-requests some of them before the refresh completes.
if (!editsQueued && !refreshInProgress) {
// Track which slot was released most recently
lastSlotReleased = slot;
// See if the number of released slots has exceeded the cache size. In practice there will be more
// live slots than retained slots, so this is just a heuristic to periodically shrink the cache.
if (releasedSlots > cacheSize && !reduceReleasedSlotCountPosted) {
reduceReleasedSlotCountPosted = true;
setImmediate(function () {
reduceReleasedSlotCountPosted = false;
reduceReleasedSlotCount();
});
}
}
}
}
// Notifications
function forEachBindingRecord(callback) {
for (var listBindingID in bindingMap) {
callback(bindingMap[listBindingID]);
}
}
function forEachBindingRecordOfSlot(slot, callback) {
for (var listBindingID in slot.bindingMap) {
callback(slot.bindingMap[listBindingID].bindingRecord, listBindingID);
}
}
function handlerToNotify(bindingRecord) {
//#DBG _ASSERT(bindingRecord.notificationHandler);
if (!bindingRecord.notificationsSent) {
bindingRecord.notificationsSent = true;
if (bindingRecord.notificationHandler.beginNotifications) {
bindingRecord.notificationHandler.beginNotifications();
}
}
return bindingRecord.notificationHandler;
}
function finishNotifications() {
if (!editsInProgress && !dataNotificationsInProgress) {
forEachBindingRecord(function (bindingRecord) {
if (bindingRecord.notificationsSent) {
//#DBG _ASSERT(bindingRecord.notificationHandler);
bindingRecord.notificationsSent = false;
if (bindingRecord.notificationHandler.endNotifications) {
bindingRecord.notificationHandler.endNotifications();
}
}
});
}
}
function handleForBinding(slot, listBindingID) {
var bindingMap = slot.bindingMap;
if (bindingMap) {
var slotBinding = bindingMap[listBindingID];
if (slotBinding) {
var handle = slotBinding.handle;
if (handle) {
return handle;
}
}
}
return slot.handle;
}
function itemForBinding(item, handle) {
if (item && item.handle !== handle) {
item = Object.create(item);
defineHandleProperty(item, handle);
}
return item;
}
function changeCount(count) {
var oldCount = knownCount;
knownCount = count;
forEachBindingRecord(function (bindingRecord) {
if (bindingRecord.notificationHandler && bindingRecord.notificationHandler.countChanged) {
handlerToNotify(bindingRecord).countChanged(knownCount, oldCount);
}
});
}
function sendIndexChangedNotifications(slot, indexOld) {
forEachBindingRecordOfSlot(slot, function (bindingRecord, listBindingID) {
//#DBG _ASSERT(bindingRecord.notificationHandler);
if (bindingRecord.notificationHandler.indexChanged) {
handlerToNotify(bindingRecord).indexChanged(handleForBinding(slot, listBindingID), slot.index, indexOld);
}
});
}
function changeSlotIndex(slot, index) {
//#DBG _ASSERT(indexUpdateDeferred || ((typeof slot.index !== "number" || indexMap[slot.index] === slot) && !indexMap[index]));
var indexOld = slot.index;
if (indexOld !== undefined && indexMap[indexOld] === slot) {
// Remove the slot's old index from the indexMap
delete indexMap[indexOld];
}
// Tolerate NaN, so clients can pass (undefined - 1) or (undefined + 1)
if (+index === index) {
setSlotIndex(slot, index, indexMap);
} else if (+indexOld === indexOld) {
//#DBG _ASSERT(!slot.indexRequested);
delete slot.index;
} else {
// If neither the new index or the old index is defined then there was no index changed.
return;
}
sendIndexChangedNotifications(slot, indexOld);
}
function insertionNotificationRecipients(slot, slotPrev, slotNext, mergeWithPrev, mergeWithNext) {
var bindingMapRecipients = {};
// Start with the intersection of the bindings for the two adjacent slots
if ((mergeWithPrev || !slotPrev.lastInSequence) && (mergeWithNext || !slotNext.firstInSequence)) {
if (slotPrev === slotsStart) {
if (slotNext === slotListEnd) {
// Special case: if the list was empty, broadcast the insertion to all ListBindings with
// notification handlers.
for (var listBindingID in bindingMap) {
bindingMapRecipients[listBindingID] = bindingMap[listBindingID];
}
} else {
// Include every binding on the next slot
for (var listBindingID in slotNext.bindingMap) {
bindingMapRecipients[listBindingID] = bindingMap[listBindingID];
}
}
} else if (slotNext === slotListEnd || slotNext.bindingMap) {
for (var listBindingID in slotPrev.bindingMap) {
if (slotNext === slotListEnd || slotNext.bindingMap[listBindingID]) {
bindingMapRecipients[listBindingID] = bindingMap[listBindingID];
}
}
}
}
// Use the union of that result with the bindings for the slot being inserted
for (var listBindingID in slot.bindingMap) {
bindingMapRecipients[listBindingID] = bindingMap[listBindingID];
}
return bindingMapRecipients;
}
function sendInsertedNotification(slot) {
var slotPrev = slot.prev,
slotNext = slot.next,
bindingMapRecipients = insertionNotificationRecipients(slot, slotPrev, slotNext),
listBindingID;
for (listBindingID in bindingMapRecipients) {
var bindingRecord = bindingMapRecipients[listBindingID];
if (bindingRecord.notificationHandler) {
handlerToNotify(bindingRecord).inserted(bindingRecord.itemPromiseFromKnownSlot(slot),
slotPrev.lastInSequence || slotPrev === slotsStart ? null : handleForBinding(slotPrev, listBindingID),
slotNext.firstInSequence || slotNext === slotListEnd ? null : handleForBinding(slotNext, listBindingID)
);
}
}
}
function changeSlot(slot) {
var itemOld = slot.item;
prepareSlotItem(slot);
forEachBindingRecordOfSlot(slot, function (bindingRecord, listBindingID) {
//#DBG _ASSERT(bindingRecord.notificationHandler);
var handle = handleForBinding(slot, listBindingID);
handlerToNotify(bindingRecord).changed(itemForBinding(slot.item, handle), itemForBinding(itemOld, handle));
});
}
function moveSlot(slot, slotMoveBefore, mergeWithPrev, mergeWithNext, skipNotifications) {
var slotMoveAfter = slotMoveBefore.prev,
listBindingID;
// If the slot is being moved before or after itself, adjust slotMoveAfter or slotMoveBefore accordingly. If
// nothing is going to change in the slot list, don't send a notification.
if (slotMoveBefore === slot) {
if (!slot.firstInSequence || !mergeWithPrev) {
return;
}
slotMoveBefore = slot.next;
} else if (slotMoveAfter === slot) {
if (!slot.lastInSequence || !mergeWithNext) {
return;
}
slotMoveAfter = slot.prev;
}
if (!skipNotifications) {
// Determine which bindings to notify
var bindingMapRecipients = insertionNotificationRecipients(slot, slotMoveAfter, slotMoveBefore, mergeWithPrev, mergeWithNext);
// Send the notification before the move
for (listBindingID in bindingMapRecipients) {
var bindingRecord = bindingMapRecipients[listBindingID];
//#DBG _ASSERT(bindingRecord.notificationHandler);
handlerToNotify(bindingRecord).moved(bindingRecord.itemPromiseFromKnownSlot(slot),
((slotMoveAfter.lastInSequence || slotMoveAfter === slot.prev) && !mergeWithPrev) || slotMoveAfter === slotsStart ? null : handleForBinding(slotMoveAfter, listBindingID),
((slotMoveBefore.firstInSequence || slotMoveBefore === slot.next) && !mergeWithNext) || slotMoveBefore === slotListEnd ? null : handleForBinding(slotMoveBefore, listBindingID)
);
}
// If a ListBinding cursor is at the slot that's moving, adjust the cursor
forEachBindingRecord(function (bindingRecord) {
bindingRecord.adjustCurrentSlot(slot);
});
}
removeSlot(slot);
insertAndMergeSlot(slot, slotMoveBefore, mergeWithPrev, mergeWithNext);
}
function deleteSlot(slot, mirage) {
//#DBG _ASSERT((!slot.fetchListeners && !slot.directFetchListeners) || !slot.item);
completeFetchPromises(slot, true);
forEachBindingRecordOfSlot(slot, function (bindingRecord, listBindingID) {
//#DBG _ASSERT(bindingRecord.notificationHandler);
handlerToNotify(bindingRecord).removed(handleForBinding(slot, listBindingID), mirage);
});
// If a ListBinding cursor is at the slot that's being removed, adjust the cursor
forEachBindingRecord(function (bindingRecord) {
bindingRecord.adjustCurrentSlot(slot);
});
removeSlotPermanently(slot);
}
function deleteMirageSequence(slot) {
// Remove the slots in order
while (!slot.firstInSequence) {
slot = slot.prev;
}
//#DBG _ASSERT(slot !== slotsStart);
var last;
do {
last = slot.lastInSequence;
var slotNext = slot.next;
deleteSlot(slot, true);
slot = slotNext;
} while (!last);
}
// Deferred Index Updates
// Returns the index of the slot taking into account any outstanding index updates
function adjustedIndex(slot) {
var undefinedIndex;
if (!slot) {
return undefinedIndex;
}
var delta = 0;
while (!slot.firstInSequence) {
//#DBG _ASSERT(typeof slot.indexNew !== "number");
delta++;
slot = slot.prev;
}
return (
typeof slot.indexNew === "number" ?
slot.indexNew + delta :
typeof slot.index === "number" ?
slot.index + delta :
undefinedIndex
);
}
// Updates the new index of the first slot in each sequence after the given slot
function updateNewIndicesAfterSlot(slot, indexDelta) {
// Adjust all the indexNews after this slot
for (slot = slot.next; slot; slot = slot.next) {
if (slot.firstInSequence) {
var indexNew = (slot.indexNew !== undefined ? slot.indexNew : slot.index);
if (indexNew !== undefined) {
slot.indexNew = indexNew + indexDelta;
}
}
}
// Adjust the overall count
countDelta += indexDelta;
indexUpdateDeferred = true;
// Increment currentRefreshID so any outstanding fetches don't cause trouble. If a refresh is in progress,
// restart it (which will also increment currentRefreshID).
if (refreshInProgress) {
beginRefresh();
} else {
currentRefreshID++;
}
}
// Updates the new index of the given slot if necessary, and all subsequent new indices
function updateNewIndices(slot, indexDelta) {
//#DBG _ASSERT(indexDelta !== 0);
// If this slot is at the start of a sequence, transfer the indexNew
if (slot.firstInSequence) {
var indexNew;
if (indexDelta < 0) {
// The given slot is about to be removed
indexNew = slot.indexNew;
if (indexNew !== undefined) {
delete slot.indexNew;
} else {
indexNew = slot.index;
}
if (!slot.lastInSequence) {
// Update the next slot now
slot = slot.next;
if (indexNew !== undefined) {
slot.indexNew = indexNew;
}
}
} else {
// The given slot was just inserted
if (!slot.lastInSequence) {
var slotNext = slot.next;
indexNew = slotNext.indexNew;
if (indexNew !== undefined) {
delete slotNext.indexNew;
} else {
indexNew = slotNext.index;
}
if (indexNew !== undefined) {
slot.indexNew = indexNew;
}
}
}
}
updateNewIndicesAfterSlot(slot, indexDelta);
}
// Updates the new index of the first slot in each sequence after the given new index
function updateNewIndicesFromIndex(index, indexDelta) {
//#DBG _ASSERT(indexDelta !== 0);
for (var slot = slotsStart; slot !== slotListEnd; slot = slot.next) {
var indexNew = slot.indexNew;
if (indexNew !== undefined && index <= indexNew) {
updateNewIndicesAfterSlot(slot, indexDelta);
break;
}
}
}
// Adjust the indices of all slots to be consistent with any indexNew properties, and strip off the indexNews
function updateIndices() {
var slot,
slotFirstInSequence,
indexNew;
for (slot = slotsStart; ; slot = slot.next) {
if (slot.firstInSequence) {
slotFirstInSequence = slot;
if (slot.indexNew !== undefined) {
indexNew = slot.indexNew;
delete slot.indexNew;
if (isNaN(indexNew)) {
break;
}
} else {
indexNew = slot.index;
}
// See if this sequence should be merged with the previous one
if (slot !== slotsStart && slot.prev.index === indexNew - 1) {
mergeSequences(slot.prev);
}
}
if (slot.lastInSequence) {
var index = indexNew;
for (var slotUpdate = slotFirstInSequence; slotUpdate !== slot.next; slotUpdate = slotUpdate.next) {
//#DBG _ASSERT(index !== slotUpdate.index || +index !== index || indexMap[index] === slotUpdate);
if (index !== slotUpdate.index) {
changeSlotIndex(slotUpdate, index);
}
if (+index === index) {
index++;
}
}
}
if (slot === slotListEnd) {
break;
}
}
// Clear any indices on slots that were moved adjacent to slots without indices
for ( ; slot !== slotsEnd; slot = slot.next) {
if (slot.index !== undefined && slot !== slotListEnd) {
changeSlotIndex(slot, undefined);
}
}
indexUpdateDeferred = false;
if (countDelta && +knownCount === knownCount) {
if (getCountPromise) {
getCountPromise.reset();
} else {
changeCount(knownCount + countDelta);
}
countDelta = 0;
}
}
// Fetch Promises
function createFetchPromise(slot, listenersProperty, listenerID, listBindingID, onComplete) {
if (slot.item) {
return new Promise(function (complete) {
if (onComplete) {
onComplete(complete, slot.item);
} else {
complete(slot.item);
}
});
} else {
var listener = {
listBindingID: listBindingID,
retained: false
};
if (!slot[listenersProperty]) {
slot[listenersProperty] = {};
}
slot[listenersProperty][listenerID] = listener;
listener.promise = new Promise(function (complete, error) {
listener.complete = (onComplete ? function (item) {
onComplete(complete, item);
} : complete);
listener.error = error;
}, function () {
// By now the slot might have been merged with another
while (slot.slotMergedWith) {
slot = slot.slotMergedWith;
}
var fetchListeners = slot[listenersProperty];
if (fetchListeners) {
delete fetchListeners[listenerID];
// See if there are any other listeners
for (var listenerID2 in fetchListeners) {
return;
}
delete slot[listenersProperty];
}
releaseSlotIfUnrequested(slot);
});
return listener.promise;
}
}
function completePromises(item, listeners) {
for (var listenerID in listeners) {
listeners[listenerID].complete(item);
}
}
function completeFetchPromises(slot, completeSynchronously) {
var fetchListeners = slot.fetchListeners,
directFetchListeners = slot.directFetchListeners;
if (fetchListeners || directFetchListeners) {
prepareSlotItem(slot);
// By default, complete asynchronously to minimize reentrancy
var item = slot.item;
var completeOrQueuePromises = function (listeners) {
if (completeSynchronously) {
completePromises(item, listeners);
} else {
fetchCompleteCallbacks.push(function () {
completePromises(item, listeners);
});
}
}
if (directFetchListeners) {
slot.directFetchListeners = null;
completeOrQueuePromises(directFetchListeners);
}
if (fetchListeners) {
slot.fetchListeners = null;
completeOrQueuePromises(fetchListeners);
}
releaseSlotIfUnrequested(slot);
}
}
function callFetchCompleteCallbacks() {
var callbacks = fetchCompleteCallbacks;
// Clear fetchCompleteCallbacks first to avoid reentrancy problems
fetchCompleteCallbacks = [];
for (var i = 0, len = callbacks.length; i < len; i++) {
callbacks[i]();
}
}
function returnDirectFetchError(slot, error) {
var directFetchListeners = slot.directFetchListeners;
if (directFetchListeners) {
slot.directFetchListeners = null;
for (var listenerID in directFetchListeners) {
directFetchListeners[listenerID].error(error);
}
releaseSlotIfUnrequested(slot);
}
}
// Item Requests