bm-core-ui 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1624 -0
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/build/@types/index.d.ts +14078 -0
- package/build/BMCodeEditor/BMCodeEditor.js +707 -0
- package/build/BMCollectionView/BMCollectionView.js +5861 -0
- package/build/BMCollectionView/BMCollectionViewCell.js +688 -0
- package/build/BMCollectionView/BMCollectionViewFlowLayout.js +4467 -0
- package/build/BMCollectionView/BMCollectionViewLayout.js +830 -0
- package/build/BMCollectionView/BMCollectionViewLayoutAttributes.js +673 -0
- package/build/BMCollectionView/BMCollectionViewMasonryLayout.js +491 -0
- package/build/BMCollectionView/BMCollectionViewStackLayout.js +634 -0
- package/build/BMCollectionView/BMCollectionViewTileLayout.js +1121 -0
- package/build/BMCoreUI.css +2833 -0
- package/build/BMView/BMAttributedLabelView.js +310 -0
- package/build/BMView/BMLayoutConstraint_v2.5.js +1813 -0
- package/build/BMView/BMLayoutGuide.js +192 -0
- package/build/BMView/BMLayoutSizeClass.js +466 -0
- package/build/BMView/BMMenu.js +574 -0
- package/build/BMView/BMScrollView.js +247 -0
- package/build/BMView/BMTextField.js +512 -0
- package/build/BMView/BMTextFieldDelegate.js +71 -0
- package/build/BMView/BMView_v2.5.js +3572 -0
- package/build/BMView/BMViewport.js +221 -0
- package/build/BMViewLayoutEditor/BMLayoutEditor.js +6117 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorConstraintSettings.js +417 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorDelegate.js +59 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingCells.js +1653 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettings.js +1480 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingsComplexCells.js +431 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingsDelegate.js +46 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorVariablesController.js +460 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorViewGroupSettings.js +293 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorViewSettings.js +382 -0
- package/build/BMViewLayoutEditor/BMLayoutVariableProvider.js +202 -0
- package/build/BMWindow/BMConfirmationPopup.js +477 -0
- package/build/BMWindow/BMKeyboardShortcut.js +151 -0
- package/build/BMWindow/BMPopover/BMPopover.js +492 -0
- package/build/BMWindow/BMToolWindow.js +71 -0
- package/build/BMWindow/BMWindow.js +2000 -0
- package/build/Core/BMAnimationContext.js +1181 -0
- package/build/Core/BMColor.js +991 -0
- package/build/Core/BMCoreUI.js +470 -0
- package/build/Core/BMFunctionCollection.js +110 -0
- package/build/Core/BMIndexPath.js +165 -0
- package/build/Core/BMInset.js +138 -0
- package/build/Core/BMKeyPath.js +100 -0
- package/build/Core/BMPoint.js +291 -0
- package/build/Core/BMRect.js +556 -0
- package/build/Core/BMSize.js +137 -0
- package/build/iScroll/LICENSE +22 -0
- package/build/iScroll/iscroll-probe.js +2154 -0
- package/build/images/AlignBottom.png +0 -0
- package/build/images/AlignCenterX.png +0 -0
- package/build/images/AlignCenterY.png +0 -0
- package/build/images/AlignLeading.png +0 -0
- package/build/images/AlignTop.png +0 -0
- package/build/images/AlignTrailing.png +0 -0
- package/build/images/AllConstraints.png +0 -0
- package/build/images/BottomConstraint.png +0 -0
- package/build/images/CenterXConstraint.png +0 -0
- package/build/images/CenterYConstraint.png +0 -0
- package/build/images/CoreUI2.png +0 -0
- package/build/images/CoreUI2@2x.png +0 -0
- package/build/images/Desktop.png +0 -0
- package/build/images/DesktopMini.png +0 -0
- package/build/images/EqualHeight.png +0 -0
- package/build/images/EqualHorizontalSpacing.png +0 -0
- package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
- package/build/images/EqualVerticalSpacing.png +0 -0
- package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
- package/build/images/EqualWidth.png +0 -0
- package/build/images/HeightConstraint.png +0 -0
- package/build/images/InactiveConstraints.png +0 -0
- package/build/images/Layout.png +0 -0
- package/build/images/LayoutVariables.png +0 -0
- package/build/images/LeftConstraint.png +0 -0
- package/build/images/OwnConstraints.png +0 -0
- package/build/images/Phone.png +0 -0
- package/build/images/PhoneLandscape.png +0 -0
- package/build/images/PhoneLandscapeMini.png +0 -0
- package/build/images/PhoneMini.png +0 -0
- package/build/images/PhonePortrait.png +0 -0
- package/build/images/PhonePortraitMini.png +0 -0
- package/build/images/Properties.png +0 -0
- package/build/images/RightConstraint.png +0 -0
- package/build/images/SubviewConstraints.png +0 -0
- package/build/images/Tablet.png +0 -0
- package/build/images/TabletLandscape.png +0 -0
- package/build/images/TabletLandscapeMini.png +0 -0
- package/build/images/TabletMini.png +0 -0
- package/build/images/TabletPortrait.png +0 -0
- package/build/images/TabletPortraitMini.png +0 -0
- package/build/images/TopConstraint.png +0 -0
- package/build/images/WidthConstraint.png +0 -0
- package/build/index.js +40 -0
- package/lib/@types/BMCoreUI.min.d.ts +14078 -0
- package/lib/BMCoreUI.min.js +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {YES, NO, BMNumberByInterpolatingNumbersWithFraction, BMCopyProperties} from './BMCoreUI'
|
|
4
|
+
import {BMFunctionCollectionMake} from './BMFunctionCollection'
|
|
5
|
+
import 'velocity-animate'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// When set to YES, this will cause animation contexts to use Velocity.js 2 as their animation engine
|
|
9
|
+
export const BM_USE_VELOCITY2 = NO;
|
|
10
|
+
// When set to YES, this will cause animation contexts to use the web animations api as their animation engine whenever possible
|
|
11
|
+
export var BM_USE_WEB_ANIMATIONS = NO;
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
****************************************************************************************************************************************************************
|
|
15
|
+
Animations
|
|
16
|
+
****************************************************************************************************************************************************************
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
// @type BMAnimationSubscriber
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* An animation subscriber is used with animation contexts when multiple animation properties should be batched for the same animation target.<br/><br/>
|
|
25
|
+
* There is no constructor or prototype for animation subscribers. Instead, they may be any object that conforms to the subscriber specification.
|
|
26
|
+
*/
|
|
27
|
+
//function BMAnimationSubscriber() {} // <constructor>
|
|
28
|
+
|
|
29
|
+
// BMAnimationSubscriber.prototype = {
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This method is invoked by animation context
|
|
33
|
+
*/
|
|
34
|
+
// applyForContext: function(context) {
|
|
35
|
+
|
|
36
|
+
// }
|
|
37
|
+
|
|
38
|
+
// @endtype
|
|
39
|
+
|
|
40
|
+
// @type BMAnimationController
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A decorator that may be applied to properties to make them animatable using CoreUI.
|
|
44
|
+
* This decorator should be applied to properties whose type is a class that implements <code>BMAnimating</code>.
|
|
45
|
+
* When a property marked animatable is changed while an animation context is active, an animation will
|
|
46
|
+
* be registered to run for that property. Classes using this decorator must have a <code>node</code> property that
|
|
47
|
+
* returns the DOM node on which animations run.
|
|
48
|
+
*
|
|
49
|
+
* The property extended by this decorator must have a setter defined for it.
|
|
50
|
+
*
|
|
51
|
+
* Using this decorator will cause your class to gain a private underscore-prefixed version
|
|
52
|
+
* of the target property that will be used by CoreUI for storage, unless you also define
|
|
53
|
+
* a getter for the property.
|
|
54
|
+
* @param target <Object> The target object.
|
|
55
|
+
* @param key <String> The property to which this decorator will apply.
|
|
56
|
+
* @param descriptor <Object> The property descriptor of the target property.
|
|
57
|
+
*/
|
|
58
|
+
export function BMAnimatable(target, key, descriptor) {
|
|
59
|
+
|
|
60
|
+
if (!descriptor.set) throw new Error('[BMCoreUI] Incorrectly applied BMAnimatable to property ' + key + ' that doesn\'t have a setter');
|
|
61
|
+
|
|
62
|
+
if (!descriptor.get) {
|
|
63
|
+
descriptor.get = function () { return this['_' + key]; };
|
|
64
|
+
|
|
65
|
+
var oldSet = descriptor.set;
|
|
66
|
+
descriptor.set = function (value) {
|
|
67
|
+
if (BMAnimationContextGetCurrent()) {
|
|
68
|
+
let controller = BMAnimationContextGetCurrent().controllerForObject(this, {node: this.node || document.body});
|
|
69
|
+
controller.registerAnimatableProperty(key, {targetValue: value.copy(), startingValue: this[key].copy()});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
oldSet.call(this, value);
|
|
73
|
+
this['_' + key] = value.copy();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
var oldSet = descriptor.set;
|
|
79
|
+
descriptor.set = function (value) {
|
|
80
|
+
if (BMAnimationContextGetCurrent()) {
|
|
81
|
+
let controller = BMAnimationContextGetCurrent().controllerForObject(this, {node: this.node || document.body});
|
|
82
|
+
controller.registerAnimatableProperty(key, {targetValue: value.copy(), startingValue: this[key].copy()});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
oldSet.call(this, value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A decorator that may be applied to properties to make them animatable using CoreUI.
|
|
94
|
+
* This decorator should be applied to properties whose type is a number.
|
|
95
|
+
* When a property marked animatable is changed while an animation context is active, an animation will
|
|
96
|
+
* be registered to run for that property. Classes using this decorator must have a <code>node</code> property that
|
|
97
|
+
* returns the DOM node on which animations run.
|
|
98
|
+
*
|
|
99
|
+
* The property extended by this decorator must have a setter defined for it.
|
|
100
|
+
*
|
|
101
|
+
* Using this decorator will cause your class to gain a private underscore-prefixed version
|
|
102
|
+
* of the target property that will be used by CoreUI for storage, unless you also define
|
|
103
|
+
* a getter for the property.
|
|
104
|
+
* @param target <Object> The target object.
|
|
105
|
+
* @param key <String> The property to which this decorator will apply.
|
|
106
|
+
* @param descriptor <Object> The property descriptor of the target property.
|
|
107
|
+
*/
|
|
108
|
+
export function BMAnimatableNumber(target, key, descriptor) {
|
|
109
|
+
|
|
110
|
+
if (!descriptor.set) throw new Error('[BMCoreUI] Incorrectly applied BMAnimatable to property ' + key + ' that doesn\'t have a setter');
|
|
111
|
+
|
|
112
|
+
if (!descriptor.get) {
|
|
113
|
+
descriptor.get = function () { return this['_' + key]; };
|
|
114
|
+
|
|
115
|
+
var oldSet = descriptor.set;
|
|
116
|
+
descriptor.set = function (value) {
|
|
117
|
+
if (BMAnimationContextGetCurrent()) {
|
|
118
|
+
let controller = BMAnimationContextGetCurrent().controllerForObject(this, {node: this.node || document.body});
|
|
119
|
+
controller.registerOwnProperty(key, {targetValue: value, startingValue: this[key] || 0});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
oldSet.call(this, value);
|
|
123
|
+
this['_' + key] = value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
var oldSet = descriptor.set;
|
|
129
|
+
descriptor.set = function (value) {
|
|
130
|
+
if (BMAnimationContextGetCurrent()) {
|
|
131
|
+
let controller = BMAnimationContextGetCurrent().controllerForObject(this, {node: this.node || document.body});
|
|
132
|
+
controller.registerOwnProperty(key, {targetValue: value, startingValue: this[key] || 0});
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
oldSet.call(this, value);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* An animation controller is an object that makes it easier to integrate custom properties with the animation engine.
|
|
144
|
+
*
|
|
145
|
+
* Animation controllers are never created explicitly, instead all animation contexts provide a method that creates
|
|
146
|
+
* an animation controller for an object. After retrieving an animation controller, you may use it to register either regular
|
|
147
|
+
* CSS/transform properties or custom properties with an update handler.
|
|
148
|
+
* To retrieve an animation controller for an object, invoke the `controllerForObject(_)` method on the active animation context,
|
|
149
|
+
* passing in the caller object as the parameter. If a previous animation controller was already retrieved for that object, the
|
|
150
|
+
* method will return that existing instance already.
|
|
151
|
+
*
|
|
152
|
+
* Besides the animation controller, in supported environments you may annotate your properties with the
|
|
153
|
+
* <code>@BMAnimatable</code> or <code>@BMAnimatableNumber</code> decorators. For properties annotated in this way,
|
|
154
|
+
* CoreUI automatically handles checking for animation conexts, retrieving animation controllers and setting up
|
|
155
|
+
* progress handlers.
|
|
156
|
+
*/
|
|
157
|
+
export function BMAnimationController() {} // <constructor>
|
|
158
|
+
|
|
159
|
+
BMAnimationController.prototype = {
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The animation context to which this animation controller applies.
|
|
163
|
+
*/
|
|
164
|
+
_animation: undefined, // <BMAnimationContext>
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The object for which this animation controller was created.
|
|
168
|
+
*/
|
|
169
|
+
_owner: undefined, // <AnyObject>
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The DOM node on which the animation will run.
|
|
173
|
+
*/
|
|
174
|
+
_node: undefined, // <DOMNode>
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* A promise that resolves when the animation initiated by this controller finishes.
|
|
178
|
+
*/
|
|
179
|
+
_promise: undefined, // <Promise<Void>>
|
|
180
|
+
|
|
181
|
+
get promise() {
|
|
182
|
+
return this._promise;
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Used internally to resolve the promise used by this controller.
|
|
187
|
+
*/
|
|
188
|
+
_resolve: undefined, // <Function>
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* An object containing the properties that are registered to this animation controller.
|
|
192
|
+
* For standard properties, the key name is the property name and its value is the usual
|
|
193
|
+
* `Velocity.js` value.
|
|
194
|
+
* For custom properties, a single `tween` property is created whose value is another object where
|
|
195
|
+
* its keys are the names of the custom properties and their values are functions that control what
|
|
196
|
+
* happens when the property value is updated.
|
|
197
|
+
*/
|
|
198
|
+
_properties: undefined, // <Object<String, AnyObject>>
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Initializes this animation controller with the given owner and animation context.
|
|
202
|
+
* @param animation <BMAnimationContext> The animation context to which this controller will apply.
|
|
203
|
+
* {
|
|
204
|
+
* @param owner <AnyObject> The object to which this animation controller is associated.
|
|
205
|
+
* @param node <DOMNode> The DOM node upon which the animation will run.
|
|
206
|
+
* }
|
|
207
|
+
* @return <BMAnimationController> This animation controller.
|
|
208
|
+
*/
|
|
209
|
+
initWithAnimation(animation, args) {
|
|
210
|
+
this._animation = animation;
|
|
211
|
+
this._owner = args.owner;
|
|
212
|
+
this._node = args.node;
|
|
213
|
+
this._properties = {};
|
|
214
|
+
this._promise = new Promise(resolve => this._resolve = resolve);
|
|
215
|
+
return this;
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Registers an animation that will run on the given standard CSS or transform property.
|
|
220
|
+
* @param property <String> The name of the property.
|
|
221
|
+
* {
|
|
222
|
+
* @param withValue <AnyObject> The value to which the property's value will animate or any of Velocity's animation syntaxes.
|
|
223
|
+
* }
|
|
224
|
+
*/
|
|
225
|
+
registerBuiltInProperty(property, args) {
|
|
226
|
+
this._properties[property] = args.withValue;
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Registers an animation that will run on the given standard CSS or transform properties.
|
|
231
|
+
* @param properties <Dictionary<AnyObject>> A dictionary whose keys are property names and their values
|
|
232
|
+
* are the values to which each property will animate.
|
|
233
|
+
*/
|
|
234
|
+
registerBuiltInPropertiesWithDictionary(properties) {
|
|
235
|
+
for (let key in properties) this.registerBuiltInProperty(key, {withValue: properties[key]});
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Registers an animation that will run on the given custom property.
|
|
240
|
+
* @param property <String> The name of the custom property.
|
|
241
|
+
* {
|
|
242
|
+
* @param withHandler <void ^(number, number)>
|
|
243
|
+
* A handler that is invoked on each animation frame, where the developer should perform their changes.
|
|
244
|
+
* This handler is passed the following parameters:
|
|
245
|
+
* * __fraction__: <Number> - The animation completion percentage. This will typically be a number between 0 and 1,
|
|
246
|
+
* however for animations that overshoot or undershoot their values, this completion
|
|
247
|
+
* fraction may surpass those values as well.
|
|
248
|
+
* * __value__: <Number, nullable> - Supplied if the property was registered with a starting and ending value.
|
|
249
|
+
* Represents the current interpolated value.
|
|
250
|
+
* @param startingValue <Number, nullable> An optional starting value from which the property will animate.
|
|
251
|
+
* If this argument is specified, `targetValue` should also be specified.
|
|
252
|
+
* @param targetValue <Number, nullable> An optional ending value to which the property will animate.
|
|
253
|
+
* If this argument is specified, `startingValue` should also be specified.
|
|
254
|
+
* }
|
|
255
|
+
*/
|
|
256
|
+
registerCustomProperty(property, args) {
|
|
257
|
+
if (!this._properties.tween) this._properties.tween = {};
|
|
258
|
+
|
|
259
|
+
if (BM_USE_WEB_ANIMATIONS) console.warn(`[BMAnimationController] Registering custom property ${property} with handler ${args.withHandler} which will run in legacy mode.`);
|
|
260
|
+
|
|
261
|
+
this._properties.tween[property] = {
|
|
262
|
+
handler: args.withHandler,
|
|
263
|
+
startingValue: args.startingValue,
|
|
264
|
+
targetValue: args.targetValue,
|
|
265
|
+
BMAnimatable: NO
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Registers an animation that will run on one of the target object's own properties.
|
|
271
|
+
* @param property <String> The name of the custom property.
|
|
272
|
+
* {
|
|
273
|
+
* @param targetValue <Number> The value to which this property will animate.
|
|
274
|
+
* @param startingValue <Number, nullable> Defaults to the property's value at the time when the animation begins.
|
|
275
|
+
* An optional starting value from which the property will animate.
|
|
276
|
+
* }
|
|
277
|
+
*/
|
|
278
|
+
registerOwnProperty(property, args) {
|
|
279
|
+
if (!this._properties.tween) this._properties.tween = {};
|
|
280
|
+
|
|
281
|
+
if (BM_USE_WEB_ANIMATIONS) console.warn(`[BMAnimationController] Registering own property ${property} which will run in legacy mode.`);
|
|
282
|
+
|
|
283
|
+
var self = this;
|
|
284
|
+
|
|
285
|
+
this._properties.tween[property] = {
|
|
286
|
+
handler: (fraction, value) => {
|
|
287
|
+
self._owner[property] = value;
|
|
288
|
+
},
|
|
289
|
+
startingValue: args.startingValue,
|
|
290
|
+
targetValue: args.targetValue,
|
|
291
|
+
name: property,
|
|
292
|
+
BMAnimatable: NO
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Registers an animation that will run on one of the target object's own properties.
|
|
298
|
+
* @param property <String> The name of the custom property.
|
|
299
|
+
* {
|
|
300
|
+
* @param targetValue <BMAnimating> The value to which this property will animate.
|
|
301
|
+
* @param startingValue <BMAnimating, nullable> Defaults to the property's value at the time when the animation begins.
|
|
302
|
+
* An optional starting value from which the property will animate.
|
|
303
|
+
* }
|
|
304
|
+
*/
|
|
305
|
+
registerAnimatableProperty(property, args) {
|
|
306
|
+
if (!this._properties.tween) this._properties.tween = {};
|
|
307
|
+
|
|
308
|
+
if (BM_USE_WEB_ANIMATIONS) console.warn(`[BMAnimationController] Registering animatable property ${property} which will run in legacy mode.`);
|
|
309
|
+
|
|
310
|
+
var self = this;
|
|
311
|
+
|
|
312
|
+
this._properties.tween[property] = {
|
|
313
|
+
// For types that implement BMAnimating, CoreUI will handle the interpolation
|
|
314
|
+
handler: (fraction, value) => {
|
|
315
|
+
self._owner[property] = value;
|
|
316
|
+
},
|
|
317
|
+
startingValue: args.startingValue,
|
|
318
|
+
targetValue: args.targetValue,
|
|
319
|
+
name: property,
|
|
320
|
+
BMAnimatable: YES
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
// @override - BMAnimationSubscriber
|
|
325
|
+
apply(animation) {
|
|
326
|
+
// All properties other than tween use the default Velocity.js value types
|
|
327
|
+
this._options = {
|
|
328
|
+
complete: () => {
|
|
329
|
+
this._resolve();
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
if ('tween' in this._properties) {
|
|
334
|
+
|
|
335
|
+
// Tween is converted into a single property, with the progress handler taking care of
|
|
336
|
+
// actually applying the properties
|
|
337
|
+
var value = this._properties.tween;
|
|
338
|
+
this._properties.tween = [1, 0];
|
|
339
|
+
|
|
340
|
+
var self = this;
|
|
341
|
+
var progressHandler = BMFunctionCollectionMake();
|
|
342
|
+
Object.getOwnPropertyNames(value).forEach((key) => {
|
|
343
|
+
var property = value[key];
|
|
344
|
+
if (!property.name) {
|
|
345
|
+
if (typeof property.startingValue == 'number' && typeof property.targetValue == 'number') {
|
|
346
|
+
progressHandler.push((elements, complete, remaining, start, fraction) => {
|
|
347
|
+
property.handler(fraction, BMNumberByInterpolatingNumbersWithFraction(property.startingValue, property.targetValue, fraction));
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
progressHandler.push((elements, complete, remaining, start, fraction) => {
|
|
352
|
+
property.handler(fraction);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else if (property.BMAnimatable) {
|
|
357
|
+
var startingValue = property.startingValue;
|
|
358
|
+
if (!startingValue) {
|
|
359
|
+
startingValue = self._owner[property.name].copy();
|
|
360
|
+
}
|
|
361
|
+
progressHandler.push((elements, complete, remaining, start, fraction) => {
|
|
362
|
+
property.handler(fraction, startingValue.interpolatedValueWithFraction(fraction, {toValue: property.targetValue}));
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
var startingValue = property.startingValue;
|
|
367
|
+
if (startingValue === undefined) {
|
|
368
|
+
startingValue = self._owner[property.name];
|
|
369
|
+
}
|
|
370
|
+
progressHandler.push((elements, complete, remaining, start, fraction) => {
|
|
371
|
+
property.handler(fraction, BMNumberByInterpolatingNumbersWithFraction(startingValue, property.targetValue, fraction));
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
this._options.progress = progressHandler;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
animation.targets.push({element: this._node, properties: this._properties, options: this._options});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// @endtype
|
|
385
|
+
|
|
386
|
+
// @type interface BMAnimationTarget
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* This interface represents an object that specifies a portion of an animation.
|
|
390
|
+
*/
|
|
391
|
+
// function BMAnimationTarget() {} // <constructor>
|
|
392
|
+
|
|
393
|
+
// {
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* The jQuery element that will be affected by this animation.
|
|
397
|
+
*/
|
|
398
|
+
// element: undefined, // <$>
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* A dictionary of properties that will be changed by the animation.
|
|
402
|
+
*/
|
|
403
|
+
// properties: undefined, // <Dictionary<any>>
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* An optional map of options that will override the animation context's options.
|
|
407
|
+
*/
|
|
408
|
+
// options: undefined, // <Dictionary<any>, nullable>
|
|
409
|
+
|
|
410
|
+
// }
|
|
411
|
+
|
|
412
|
+
// @endtype
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
// @type BMAnimationEasing extends String
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* An enum containing predefined easings. These all resolve to bezier curves that are compatible
|
|
419
|
+
* with the Web Animations API.
|
|
420
|
+
*/
|
|
421
|
+
const BMAnimationEasing = Object.freeze({ // <enum>
|
|
422
|
+
ease: "cubic-bezier(0.25, 0.1, 0.25, 1)",
|
|
423
|
+
easeIn: "cubic-bezier(0.42, 0, 1, 1)",
|
|
424
|
+
easeOut: "cubic-bezier(0, 0, 0.58, 1)",
|
|
425
|
+
easeInOut: "cubic-bezier(0.42, 0, 0.58, 1)",
|
|
426
|
+
easeInSine: "cubic-bezier(0.47, 0, 0.745, 0.715)",
|
|
427
|
+
easeOutSine: "cubic-bezier(0.39, 0.575, 0.565, 1)",
|
|
428
|
+
easeInOutSine: "cubic-bezier(0.445, 0.05, 0.55, 0.95)",
|
|
429
|
+
easeInQuad: "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
|
|
430
|
+
easeOutQuad: "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
|
|
431
|
+
easeInOutQuad: "cubic-bezier(0.455, 0.03, 0.515, 0.955)",
|
|
432
|
+
easeInCubic: "cubic-bezier(0.55, 0.055, 0.675, 0.19)",
|
|
433
|
+
easeOutCubic: "cubic-bezier(0.215, 0.61, 0.355, 1)",
|
|
434
|
+
easeInOutCubic: "cubic-bezier(0.645, 0.045, 0.355, 1)",
|
|
435
|
+
easeInQuart: "cubic-bezier(0.895, 0.03, 0.685, 0.22)",
|
|
436
|
+
easeOutQuart: "cubic-bezier(0.165, 0.84, 0.44, 1)",
|
|
437
|
+
easeInOutQuart: "cubic-bezier(0.77, 0, 0.175, 1)",
|
|
438
|
+
easeInQuint: "cubic-bezier(0.755, 0.05, 0.855, 0.06)",
|
|
439
|
+
easeOutQuint: "cubic-bezier(0.23, 1, 0.32, 1)",
|
|
440
|
+
easeInOutQuint: "cubic-bezier(0.86, 0, 0.07, 1)",
|
|
441
|
+
easeInExpo: "cubic-bezier(0.95, 0.05, 0.795, 0.035)",
|
|
442
|
+
easeOutExpo: "cubic-bezier(0.19, 1, 0.22, 1)",
|
|
443
|
+
easeInOutExpo: "cubic-bezier(1, 0, 0, 1)",
|
|
444
|
+
easeInCirc: "cubic-bezier(0.6, 0.04, 0.98, 0.335)",
|
|
445
|
+
easeOutCirc: "cubic-bezier(0.075, 0.82, 0.165, 1)",
|
|
446
|
+
easeInOutCirc: "cubic-bezier(0.785, 0.135, 0.15, 0.86)"
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// @endtype
|
|
450
|
+
|
|
451
|
+
// These values are used to combine transform properties into a single transform string
|
|
452
|
+
/* @private */ const BMAnimationTransformPropertyDefaults = Object.freeze({
|
|
453
|
+
scaleX: 1,
|
|
454
|
+
scaleY: 1,
|
|
455
|
+
scaleZ: 1,
|
|
456
|
+
rotate: 0,
|
|
457
|
+
rotateX: 0,
|
|
458
|
+
rotateY: 0,
|
|
459
|
+
rotateZ: 0,
|
|
460
|
+
skewX: 0,
|
|
461
|
+
skewY: 0,
|
|
462
|
+
skewZ: 0,
|
|
463
|
+
translateZ: 0,
|
|
464
|
+
translateX: 0,
|
|
465
|
+
translateY: 0
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// These values are used to combine filter properties into a single filter string
|
|
469
|
+
/* @private */ const BMAnimationFilterPropertyDefaults = Object.freeze({
|
|
470
|
+
blur: 1,
|
|
471
|
+
brightness: 1,
|
|
472
|
+
contrast: 1,
|
|
473
|
+
'drop-shadow': 0,
|
|
474
|
+
grayscale: 0,
|
|
475
|
+
'hue-rotate': 0,
|
|
476
|
+
invert: 0,
|
|
477
|
+
saturate: 0,
|
|
478
|
+
sepia: 0
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Convenience method used in converting from velocity calls to BMAnimationContext - only used internally.
|
|
482
|
+
// This uses almost the same syntax as velocity js but supports some of the features of BMAnimationContext, such as using web animations.
|
|
483
|
+
export function __BMVelocityAnimate(node, properties, options, useWebAnimations) {
|
|
484
|
+
return BMAnimateWithBlock(() => {
|
|
485
|
+
let controller = BMAnimationContextGetCurrent().controllerForObject(node, {node: node});
|
|
486
|
+
controller.registerBuiltInPropertiesWithDictionary(properties);
|
|
487
|
+
|
|
488
|
+
if (useWebAnimations) BMAnimationContextEnableWebAnimations();
|
|
489
|
+
}, options);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// @type BMAnimationContext
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Allows using <code>$.Velocity.hook</code> by passing in a key-value object similar to `$.fn.css`.
|
|
496
|
+
* @param element <$ or DOMNode> The jQuery element or DOM node on which to apply the properties.
|
|
497
|
+
* @param properties <Object> The properties map.
|
|
498
|
+
*/
|
|
499
|
+
export function BMHook(element, properties) {
|
|
500
|
+
if (BM_USE_VELOCITY2) {
|
|
501
|
+
let node = element;
|
|
502
|
+
if (element instanceof window.$) {
|
|
503
|
+
node = element[0];
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// In Velocity 2.0 mode, transforms are no longer supported as individual properties and old .hook() function is gone
|
|
507
|
+
// In this case, use the same principle as web animations, combining transform and filter properties and applying them
|
|
508
|
+
// as standard CSS properties
|
|
509
|
+
let webAnimationProperties = {};
|
|
510
|
+
|
|
511
|
+
let transformProperty = '';
|
|
512
|
+
for (let key in properties) {
|
|
513
|
+
if (key in BMAnimationTransformPropertyDefaults) {
|
|
514
|
+
transformProperty += `${key}(${properties[key]}) `;
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
webAnimationProperties[key] = properties[key];
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (transformProperty) webAnimationProperties.transform = transformProperty;
|
|
521
|
+
|
|
522
|
+
let filterProperty = '';
|
|
523
|
+
for (let key in properties) {
|
|
524
|
+
if (key in BMAnimationFilterPropertyDefaults) {
|
|
525
|
+
filterProperty += `${key}(${properties[key]}) `;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (filterProperty) webAnimationProperties.filter = filterProperty;
|
|
529
|
+
|
|
530
|
+
BMCopyProperties(node.style, webAnimationProperties);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
for (var key in properties) {
|
|
534
|
+
(window.Velocity || $.Velocity).hook(element, key, properties[key]);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* An animation context manages the options and animated elements for an animation.
|
|
541
|
+
* Animation contexts are never created explicitly, instead they are created and activated in the background by
|
|
542
|
+
* <code>BMAnimationBeginWithDuration()</code> or <code>BMAnimateWithBlock()</code>.
|
|
543
|
+
*
|
|
544
|
+
* While there is an active animation context, changing compatible properties will be done through an animation, using
|
|
545
|
+
* that context's animation options.
|
|
546
|
+
*
|
|
547
|
+
* To support animating with animation contexts, the current animation context may be retrieved by invoking the global <code>BMAnimationContextGetCurrent()</code>
|
|
548
|
+
* function. This will return the current animation context if one is active. That context may be used to register animations in response to property changes.
|
|
549
|
+
*/
|
|
550
|
+
export function BMAnimationContext() { // <constructor>
|
|
551
|
+
throw new Error("Animation contexts may not be created manually. To create an animation context, invoke the BMAnimationBeginWithDuration() function.");
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
BMAnimationContext.prototype = {
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* @private
|
|
558
|
+
* The animation options for this context.
|
|
559
|
+
*/
|
|
560
|
+
options: {}, // <Object>
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @private
|
|
564
|
+
* A map containing the callbacks for animations that are registered and should be applied when this animation begins.
|
|
565
|
+
* The keys in this map may be any type of object and their values must be objects that contain the <code>apply</code> method which will
|
|
566
|
+
* be invoked when this animation is about to begin.
|
|
567
|
+
*/
|
|
568
|
+
subscribers: undefined, // <Map<AnyObject, BMAnimationSubscriber>>
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Returns an animation controller for the given object. If an animation controller was previously requested
|
|
572
|
+
* for this object for this animation, this method will return that previously created controller.
|
|
573
|
+
* @param object <AnyObject> The object which will make use of the animation controller.
|
|
574
|
+
* {
|
|
575
|
+
* @param node <DOMNode> The node upon which the animation will run.
|
|
576
|
+
* }
|
|
577
|
+
* @return <BMAnimationController> An animation controller.
|
|
578
|
+
*/
|
|
579
|
+
controllerForObject(object, args) {
|
|
580
|
+
// Get the current subscriber for this object
|
|
581
|
+
let subscriber = this.subscribers.get(object);
|
|
582
|
+
|
|
583
|
+
if (!subscriber) {
|
|
584
|
+
// If one isn't available, create it and associate it to this object
|
|
585
|
+
subscriber = Object.create(BMAnimationController.prototype).initWithAnimation(this, {owner: object, node: args.node});
|
|
586
|
+
this.subscribers.set(object, subscriber);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return subscriber;
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* @private
|
|
594
|
+
* An array containing the animation targets for the current context.
|
|
595
|
+
* An animation target is an object with the following three properties:
|
|
596
|
+
* <ul>
|
|
597
|
+
* <li>element <$>: The jQuery node to which the animation should be applied.</li>
|
|
598
|
+
* <li>properties <Object>: An object containing the node properties which will be animated.</li>
|
|
599
|
+
* <li>options <Object>: An optional object containing animation property overrides.</li>
|
|
600
|
+
* </ul>
|
|
601
|
+
*/
|
|
602
|
+
targets: undefined, // <[BMAnimationTarget]>
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Should be invoked to register an animation that will run when this animation context is started.
|
|
606
|
+
* @param target <$ or DOMNode> The DOM node or jQuery wrapper on which this animation will run.
|
|
607
|
+
* {
|
|
608
|
+
* @param properties <Object<String, AnyObject>> An object describing the properties that will be changed by this animation.
|
|
609
|
+
* This object has the same format as the Velocity.js property object.
|
|
610
|
+
* @param options <nullable Object<String, AnyObject>> An optional object describing the animation options that this particular animation should use.
|
|
611
|
+
* Specifying this parameter will cause the animation context's animation options to be overriden for this animation.
|
|
612
|
+
* }
|
|
613
|
+
*/
|
|
614
|
+
registerAnimationForTarget: function(target, args) {
|
|
615
|
+
this.targets.push({
|
|
616
|
+
element: target,
|
|
617
|
+
properties: args.properties,
|
|
618
|
+
options: args.options
|
|
619
|
+
});
|
|
620
|
+
},
|
|
621
|
+
|
|
622
|
+
registerSubscriber: function(subscriber, args) {
|
|
623
|
+
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* The animation stack holds all animations with their attributes.
|
|
630
|
+
* Whenever an attribute property is changed while there is an active animation, that property
|
|
631
|
+
* will smoothly interpolate to the new value using the top-most animation's attributes.
|
|
632
|
+
*/
|
|
633
|
+
var _BMAnimationStack = []; // <[BMAnimationContext]>
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Retrieves the currently active animation context, if there is one.
|
|
637
|
+
* @return <BMAnimationContext, nullable> The current animation context if there is one, `undefined` otherwise or if the current animation context
|
|
638
|
+
* is static.
|
|
639
|
+
*/
|
|
640
|
+
export function BMAnimationContextGetCurrent() {
|
|
641
|
+
let context = _BMAnimationStack[_BMAnimationStack.length - 1];
|
|
642
|
+
|
|
643
|
+
if (context && context._isDisabled) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
return context;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Causes the current animation context to run in web animations mode, if available.
|
|
653
|
+
* If no animation context is currently active, this method does nothing.
|
|
654
|
+
*/
|
|
655
|
+
export function BMAnimationContextEnableWebAnimations() {
|
|
656
|
+
let context = BMAnimationContextGetCurrent();
|
|
657
|
+
if (context) context._useWebAnimations = YES;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Should be invoked to start a new static attribute animation.
|
|
662
|
+
* A static animation context supresses any existing animation context, causing property updates while it is active to not be animated.
|
|
663
|
+
* While a static animation context is active, `BMAnimationContextGetCurrent()` returns `undefined`.
|
|
664
|
+
*
|
|
665
|
+
* Like with regular animation contexts, static contexts must be applied via `BMAnimationApply()` or `BMAnimationApplyBlocking(_)`.
|
|
666
|
+
*/
|
|
667
|
+
export function BMAnimationContextBeginStatic() {
|
|
668
|
+
|
|
669
|
+
var animationContext = Object.create(BMAnimationContext.prototype);
|
|
670
|
+
|
|
671
|
+
animationContext.options = {duration: 0, easing: 'linear'};
|
|
672
|
+
animationContext.targets = [];
|
|
673
|
+
animationContext._isDisabled = YES;
|
|
674
|
+
// The subscribers property is used internally by cells and views that need to interact with the animation before it is applied
|
|
675
|
+
// It is a map of objects where cells and views add themselves as keys with arbitrary values.
|
|
676
|
+
// The subscriber value objects however must contain an 'apply()' method; this method will be invoked by the animation when it is about to be applied.
|
|
677
|
+
animationContext.subscribers = new Map();
|
|
678
|
+
|
|
679
|
+
_BMAnimationStack.push(animationContext);
|
|
680
|
+
|
|
681
|
+
return animationContext;
|
|
682
|
+
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
// @param easing <Multiple Types> The animation's easing, using any of Velocity.js supported easing formats.
|
|
687
|
+
// @param ... <> Additional Velocity.js parameters
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Should be invoked to start a new attribute animation.
|
|
691
|
+
* After this call, you should assign the new values to the attributes you want to animate
|
|
692
|
+
* and finally call <code>BMAnimationApply()</code> to start the animations.
|
|
693
|
+
* You may call <code>BMAnimationBeginWithDuration()</code> multiple times in a row to set up multiple animations, but each call must be balanced
|
|
694
|
+
* by calling <code>BMAnimationApply()</code>.
|
|
695
|
+
* @param duration <Int> The animation's duration, in milliseconds.
|
|
696
|
+
* @param options <Object, nullable> An object of animation attributes. This object is optional and contains the same keys and values as Velocity.js's animation options object.
|
|
697
|
+
* This object should not be modified afterwards until this animation is applied.
|
|
698
|
+
* @return <BMAnimationContext> The animation context associated with the new animation.
|
|
699
|
+
*/
|
|
700
|
+
export function BMAnimationBeginWithDuration(duration, options) {
|
|
701
|
+
options = options || {};
|
|
702
|
+
options.duration = duration;
|
|
703
|
+
|
|
704
|
+
var animationContext = Object.create(BMAnimationContext.prototype);
|
|
705
|
+
|
|
706
|
+
animationContext.options = options;
|
|
707
|
+
animationContext.targets = [];
|
|
708
|
+
// The subscribers property is used internally by cells and views that need to interact with the animation before it is applied
|
|
709
|
+
// It is a map of objects where cells and views add themselves as keys with arbitrary values.
|
|
710
|
+
// The subscriber value objects however must contain an 'apply()' method; this method will be invoked by the animation when it is about to be applied.
|
|
711
|
+
animationContext.subscribers = new Map();
|
|
712
|
+
|
|
713
|
+
_BMAnimationStack.push(animationContext);
|
|
714
|
+
|
|
715
|
+
return animationContext;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Registers a completion handler that will fire when the current animation context has finished running its animation.
|
|
720
|
+
* If there is no current animation context or if the current animation context is static, the provided completion handler will
|
|
721
|
+
* execute synchronously before this method returns.
|
|
722
|
+
* @param handler <void ^()> The handler to invoke.
|
|
723
|
+
*/
|
|
724
|
+
export function BMAnimationContextAddCompletionHandler(handler) {
|
|
725
|
+
let context;
|
|
726
|
+
if (context = BMAnimationContextGetCurrent()) {
|
|
727
|
+
if (context.options.complete) {
|
|
728
|
+
if (context.options.complete._isFunctionCollection) {
|
|
729
|
+
context.options.complete.push(handler);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
let functionCollection = BMFunctionCollectionMake();
|
|
733
|
+
functionCollection.push(context.options.complete);
|
|
734
|
+
functionCollection.push(handler);
|
|
735
|
+
context.options.complete = functionCollection;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
context.options.complete = BMFunctionCollectionMake();
|
|
740
|
+
context.options.complete.push(handler);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
handler();
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Must be invoked after a <code>BMAnimationBeginWithDuration()</code> call to apply the pending animation.
|
|
750
|
+
* @return <Promise<void>> A promise that resolves when the animation completes.
|
|
751
|
+
*/
|
|
752
|
+
export function BMAnimationApply() {
|
|
753
|
+
return BMAnimationApplyBlocking(NO);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Set to `YES` if the environment does not fully support web animations
|
|
757
|
+
var BM_WEB_ANIMATIONS_DISABLED = NO;
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Must be invoked after a <code>BMAnimationBeginWithDuration()</code> call to apply the pending animation.
|
|
761
|
+
* Applying the animation in this way will stop all other running animations on the animation targets.
|
|
762
|
+
* @param blocking <Boolean, nullable> Defaults to `YES`. If set to `YES`, the animation will be blocking, otherwise it will be scheduled.
|
|
763
|
+
* A blocking animation will cancel all other animations currently running on any of the target elements.
|
|
764
|
+
* @return <Promise<void>> A promise that resolves when the animation completes.
|
|
765
|
+
*/
|
|
766
|
+
export function BMAnimationApplyBlocking(blocking) {
|
|
767
|
+
// If the current context is static, pop it allowing other animation contexts to run
|
|
768
|
+
let context = _BMAnimationStack[_BMAnimationStack.length - 1];
|
|
769
|
+
if (!context) {
|
|
770
|
+
console.error('Attempted to apply an animation while there was no animation context active. This method call will be ignored.');
|
|
771
|
+
return Promise.resolve(void 0);
|
|
772
|
+
}
|
|
773
|
+
if (context._isDisabled) {
|
|
774
|
+
_BMAnimationStack.pop();
|
|
775
|
+
return Promise.resolve(void 0);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (blocking === undefined) blocking = YES;
|
|
779
|
+
|
|
780
|
+
// Give the subscribers a chance to modify the animation prior to it actually being applied
|
|
781
|
+
new Map(BMAnimationContextGetCurrent().subscribers).forEach(function (value, key, map) {
|
|
782
|
+
value.prepare && value.prepare(animation);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
var animation = _BMAnimationStack.pop();
|
|
786
|
+
var animationTargets = animation.targets;
|
|
787
|
+
|
|
788
|
+
// Notify the subscribers that the animation is about to be applied
|
|
789
|
+
var animationSubscribers = animation.subscribers;
|
|
790
|
+
animationSubscribers.forEach(function (value, key, map) {
|
|
791
|
+
value.apply(animation);
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// The complete callback should only be invoked once
|
|
795
|
+
// when the last element finishes animating
|
|
796
|
+
var completeCallback;
|
|
797
|
+
var completeAnimations = NO;
|
|
798
|
+
if (animation.options.complete) {
|
|
799
|
+
var optionsCompleteCallback = animation.options.complete;
|
|
800
|
+
completeCallback = async function (elements) {
|
|
801
|
+
if (completeAnimations) return;
|
|
802
|
+
completeAnimations = YES;
|
|
803
|
+
|
|
804
|
+
// Set timeout is used to schedule the callback to after all animations in the set have finished
|
|
805
|
+
await 0;
|
|
806
|
+
optionsCompleteCallback(elements);
|
|
807
|
+
|
|
808
|
+
for (var i = 0; i < animationTargetsLength; i++) {
|
|
809
|
+
if (animationTargets[i].options && animationTargets[i].options.complete) {
|
|
810
|
+
animationTargets[i].options.complete(elements);
|
|
811
|
+
}
|
|
812
|
+
else if (animationTargets[i].complete) {
|
|
813
|
+
animationTargets[i].complete(elements);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
completeCallback = async function (elements) {
|
|
820
|
+
if (completeAnimations) return;
|
|
821
|
+
completeAnimations = YES;
|
|
822
|
+
|
|
823
|
+
// Set timeout is used to schedule the callback to after all animations in the set have finished
|
|
824
|
+
await 0;
|
|
825
|
+
for (var i = 0; i < animationTargetsLength; i++) {
|
|
826
|
+
if (animationTargets[i].options && animationTargets[i].options.complete) {
|
|
827
|
+
animationTargets[i].options.complete(elements);
|
|
828
|
+
}
|
|
829
|
+
else if (animationTargets[i].complete) {
|
|
830
|
+
animationTargets[i].complete(elements);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
animation.options.complete = completeCallback;
|
|
836
|
+
|
|
837
|
+
if (window.event && window.event.shiftKey) {
|
|
838
|
+
animation.options.duration = animation.options.duration * 5;
|
|
839
|
+
window.event.preventDefault();
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
var stride = animation.options.stride;
|
|
843
|
+
var delay = animation.options.delay || 0;
|
|
844
|
+
|
|
845
|
+
var velocity = window.Velocity || $.Velocity;
|
|
846
|
+
var promises = [];
|
|
847
|
+
|
|
848
|
+
// An array to which web animations are added to set their start time to the same value
|
|
849
|
+
let webAnimations = [];
|
|
850
|
+
|
|
851
|
+
// Fire all the animations
|
|
852
|
+
var animationTargetsLength = animation.targets.length;
|
|
853
|
+
for (var i = 0; i < animationTargetsLength; i++) {
|
|
854
|
+
var element = animationTargets[i].element;
|
|
855
|
+
|
|
856
|
+
if (blocking) velocity(element, 'stop', true);
|
|
857
|
+
|
|
858
|
+
// Allow each target to set its own options
|
|
859
|
+
var options = animationTargets[i].options ? BMCopyProperties({}, animation.options, animationTargets[i].options) : BMCopyProperties({}, animation.options);
|
|
860
|
+
|
|
861
|
+
if (stride) {
|
|
862
|
+
options = BMCopyProperties({}, options);
|
|
863
|
+
|
|
864
|
+
if (i < animationTargetsLength - 1) {
|
|
865
|
+
delete options.complete;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
options.delay = delay;
|
|
869
|
+
delay += stride;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
delete options.complete;
|
|
873
|
+
|
|
874
|
+
if ((context._useWebAnimations || BM_USE_WEB_ANIMATIONS) && document.body.animate && !BM_WEB_ANIMATIONS_DISABLED) {
|
|
875
|
+
if (options.queue) {
|
|
876
|
+
console.warn('[BMCoreUI] Using the queue animation option with web animations which is not supported. This argument will be ignored.');
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// For web animations, extract all non-tween properties and use web animations api on them
|
|
880
|
+
// also unify all transform and filter properties into a single transform property
|
|
881
|
+
let webAnimationProperties = {};
|
|
882
|
+
|
|
883
|
+
// Extract and unify the transform properties
|
|
884
|
+
let sourceTransformProperty = '';
|
|
885
|
+
let transformProperty = '';
|
|
886
|
+
for (let key in animationTargets[i].properties) {
|
|
887
|
+
if (key in BMAnimationTransformPropertyDefaults) {
|
|
888
|
+
// Specify an initial value if the property value is forcefed
|
|
889
|
+
if (Array.isArray(animationTargets[i].properties[key])) {
|
|
890
|
+
transformProperty += `${key}(${animationTargets[i].properties[key][0]}) `;
|
|
891
|
+
sourceTransformProperty += `${key}(${animationTargets[i].properties[key][1]}) `;
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
transformProperty += `${key}(${animationTargets[i].properties[key]}) `;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// If a transform property was created, prepare it for web animations
|
|
900
|
+
if (transformProperty) {
|
|
901
|
+
// Specify the initial forcefed value if it existed
|
|
902
|
+
if (sourceTransformProperty) {
|
|
903
|
+
webAnimationProperties.transform = [transformProperty, sourceTransformProperty];
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
webAnimationProperties.transform = transformProperty;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Perform the same for filter properties
|
|
911
|
+
let filterProperty = '';
|
|
912
|
+
for (let key in animationTargets[i].properties) {
|
|
913
|
+
if (key in BMAnimationFilterPropertyDefaults) {
|
|
914
|
+
filterProperty += `${key}(${animationTargets[i].properties[key]}) `;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
if (filterProperty) webAnimationProperties.filter = filterProperty;
|
|
918
|
+
|
|
919
|
+
// Add all the other properties
|
|
920
|
+
for (let key in animationTargets[i].properties) {
|
|
921
|
+
if ((key in BMAnimationTransformPropertyDefaults) || (key in BMAnimationFilterPropertyDefaults)) continue;
|
|
922
|
+
if (key == 'tween') continue;
|
|
923
|
+
webAnimationProperties[key] = animationTargets[i].properties[key];
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Set up the animation
|
|
927
|
+
let node = element;
|
|
928
|
+
if (element instanceof window.$) {
|
|
929
|
+
node = element[0];
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (options.display == 'block') {
|
|
933
|
+
node.style.display = 'block';
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
let sourceAnimationProperties = {};
|
|
937
|
+
|
|
938
|
+
for (let key in webAnimationProperties) {
|
|
939
|
+
if (Array.isArray(webAnimationProperties[key])) {
|
|
940
|
+
// When the parameter is an array, this is interpreted as forcefeeding an initial value in velocity js
|
|
941
|
+
sourceAnimationProperties[key] = webAnimationProperties[key][1];
|
|
942
|
+
webAnimationProperties[key] = webAnimationProperties[key][0];
|
|
943
|
+
// Apply the forcefed value to the node initially, if there is any delay
|
|
944
|
+
if (options.delay) {
|
|
945
|
+
node.style[key] = webAnimationProperties[key][1];
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
else {
|
|
949
|
+
sourceAnimationProperties[key] = node.style[key];
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
const easing = Array.isArray(options.easing) ? `cubic-bezier(${options.easing.join(',')})` : BMAnimationEasing[options.easing];
|
|
954
|
+
|
|
955
|
+
let nodeAnimation;
|
|
956
|
+
try {
|
|
957
|
+
nodeAnimation = node.animate([sourceAnimationProperties, webAnimationProperties], {
|
|
958
|
+
//nodeAnimation = node.animate([{perspective: 1000}, {perspective: 1000}], {
|
|
959
|
+
duration: options.duration * (window.BMAnimationMultiplier || 1),
|
|
960
|
+
easing: easing || 'linear',
|
|
961
|
+
//fill: 'none',
|
|
962
|
+
delay: options.delay && (options.delay * (window.BMAnimationMultiplier || 1)),
|
|
963
|
+
composite: 'replace',
|
|
964
|
+
iterationComposite: 'replace'
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
catch (e) {
|
|
968
|
+
console.error('[BMCoreUI] This environment does not fully support web animations and use of this API will be disabled.');
|
|
969
|
+
// If the current browser doesn't properly support animations, web animations will be
|
|
970
|
+
// permanently disabled for this page and the sequence rerun with Velocity.js
|
|
971
|
+
BM_WEB_ANIMATIONS_DISABLED = YES;
|
|
972
|
+
i--;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// Some implementations do not have promise support for web animations
|
|
977
|
+
let hasPromiseSupport = NO;
|
|
978
|
+
if (nodeAnimation.finished) {
|
|
979
|
+
hasPromiseSupport = YES;
|
|
980
|
+
nodeAnimation.finished.then(async () => {
|
|
981
|
+
// Set up the final values when the animation finishes
|
|
982
|
+
// Animation fill mode is not used because it breaks all other CSS
|
|
983
|
+
for (let key in webAnimationProperties) {
|
|
984
|
+
node.style[key] = webAnimationProperties[key];
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// Store a reference to this animation; the start times of all of the animations will be synchronized at the end of the loop
|
|
990
|
+
webAnimations.push(nodeAnimation);
|
|
991
|
+
|
|
992
|
+
// Set up a promise and add it to the animation to maintain compatibility with the Velocity.js promise
|
|
993
|
+
let promise = new Promise((resolve, reject) => {
|
|
994
|
+
let finished = NO;
|
|
995
|
+
nodeAnimation.onfinish = function () {
|
|
996
|
+
if (finished) return;
|
|
997
|
+
finished = YES;
|
|
998
|
+
|
|
999
|
+
// If the engine does not have promise support, then the final values will be applied in onFinish
|
|
1000
|
+
if (!hasPromiseSupport) {
|
|
1001
|
+
for (let key in webAnimationProperties) {
|
|
1002
|
+
node.style[key] = webAnimationProperties[key];
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (options.display == 'none') {
|
|
1007
|
+
node.style.display = 'none';
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
//nodeAnimation.cancel();
|
|
1011
|
+
|
|
1012
|
+
resolve();
|
|
1013
|
+
};
|
|
1014
|
+
nodeAnimation.oncancel = function () {
|
|
1015
|
+
resolve();
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
// On Safari, onfinish and oncancel are bugged currently do not fire at times, so they are manually resolved as a workaround
|
|
1019
|
+
//setTimeout(nodeAnimation.onfinish, ((options.delay || 0) + options.duration) * (window.BMAnimationMultiplier || 1));
|
|
1020
|
+
});
|
|
1021
|
+
promises.push(promise);
|
|
1022
|
+
|
|
1023
|
+
// If a progress handler is specified for the options, set up an accompanying Velocity.js animation to handle it
|
|
1024
|
+
if (options.progress) {
|
|
1025
|
+
promises.push(velocity.animate(element, {tween: 1}, options));
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
}
|
|
1029
|
+
else if (BM_USE_VELOCITY2) {
|
|
1030
|
+
if (element instanceof window.$) {
|
|
1031
|
+
promises.push(element[0].velocity(animationTargets[i].properties, options));
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
promises.push(element.velocity(animationTargets[i].properties, options));
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
promises.push(velocity.animate(element, animationTargets[i].properties, options));
|
|
1039
|
+
|
|
1040
|
+
if (options.queue) {
|
|
1041
|
+
velocity.Utilities.dequeue(element, options.queue);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (window.Promise) {
|
|
1047
|
+
let promise = Promise.all(promises);
|
|
1048
|
+
promise.then(function () {
|
|
1049
|
+
if (animation.options.complete) {
|
|
1050
|
+
animation.options.complete();
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
return promise;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
//@param blocking <Boolean, nullable> Defaults to NO. If set to YES, this animation will be blocking and stop all other animations, otherwise it will be scheduled.
|
|
1059
|
+
//@param stride <Number, nullable> Defaults to 0. If set to a number, this will add stride millisecond delay between each animation. The animation delays will
|
|
1060
|
+
// be added to the elements in the order that their animations were scheduled.
|
|
1061
|
+
//@param duration <Int> The animation's duration in milliseconds.
|
|
1062
|
+
//@param ... <> Additional Velocity.js parameters
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Animates the properties modified in the given callback using the supplied options.
|
|
1066
|
+
* @param block <void (^)()> The callback in which you can modify the properties which will smoothly animate to their new values.
|
|
1067
|
+
* @param options <Object> An object of animation attributes. This object contains the same keys and values as Velocity.js's animation options object.
|
|
1068
|
+
* This object should not be modified afterwards until this animation is applied.
|
|
1069
|
+
* This object must contain a valid duration property.
|
|
1070
|
+
* @return <Promise<void>> A promise that resolves when this animation completes or is cancelled.
|
|
1071
|
+
*/
|
|
1072
|
+
export function BMAnimateWithBlock(block, options) {
|
|
1073
|
+
var context = BMAnimationBeginWithDuration(options.duration, options);
|
|
1074
|
+
|
|
1075
|
+
block();
|
|
1076
|
+
|
|
1077
|
+
if (options.blocking) {
|
|
1078
|
+
return BMAnimationApplyBlocking(YES);
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
return BMAnimationApply();
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* Converts the given element to a material design-like ripple that activates on mouse/touch down and up events on the given target element.
|
|
1089
|
+
* Though this is not required, it is recommended that the ripple element be an absolutely positioned direct descendant of the target element.
|
|
1090
|
+
* For best results, the ripple should also have its border radius set to 50% and its pointer-events set to none.
|
|
1091
|
+
* @param ripple <$> The jQuery element that will act as the ripple.
|
|
1092
|
+
* {
|
|
1093
|
+
* @param forTarget <$> The jQuery element.
|
|
1094
|
+
* }
|
|
1095
|
+
*/
|
|
1096
|
+
export function BMRippleMakeWithElement(ripple, options) {
|
|
1097
|
+
// TODO: get rid of jQuery
|
|
1098
|
+
var target = options.forTarget;
|
|
1099
|
+
|
|
1100
|
+
ripple[0].classList.add('BMRipple');
|
|
1101
|
+
|
|
1102
|
+
var startEvent = BMIsTouchDevice ? 'touchstart.ripple' : 'mousedown.ripple';
|
|
1103
|
+
var endEvent = BMIsTouchDevice ? 'touchend.ripple' : 'mouseup.ripple';
|
|
1104
|
+
|
|
1105
|
+
target.on(startEvent, function (event) {
|
|
1106
|
+
var width = target.outerWidth();
|
|
1107
|
+
var height = target.outerHeight();
|
|
1108
|
+
|
|
1109
|
+
var highestDimension = Math.max(width, height);
|
|
1110
|
+
var diagonal = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
|
|
1111
|
+
var box = target[0].getBoundingClientRect();
|
|
1112
|
+
var targetOffset = {
|
|
1113
|
+
left: box.left,
|
|
1114
|
+
top: box.top
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
event.pageX = BMIsTouchDevice ? event.originalEvent.touches[0].screenX : event.originalEvent.clientX;
|
|
1118
|
+
event.pageY = BMIsTouchDevice ? event.originalEvent.touches[0].screenY : event.originalEvent.clientY;
|
|
1119
|
+
|
|
1120
|
+
ripple.velocity("stop", true).css({
|
|
1121
|
+
width: '0px',
|
|
1122
|
+
height: '0px',
|
|
1123
|
+
opacity: 0
|
|
1124
|
+
});
|
|
1125
|
+
BMHook(ripple, {
|
|
1126
|
+
translateX: (event.pageX - targetOffset.left) + 'px',
|
|
1127
|
+
translateY: (event.pageY - targetOffset.top) + 'px'
|
|
1128
|
+
});
|
|
1129
|
+
ripple.velocity({
|
|
1130
|
+
translateX: ((width - diagonal) / 2) + 'px',
|
|
1131
|
+
translateY: ((height - diagonal) / 2) + 'px',
|
|
1132
|
+
width: diagonal + 'px',
|
|
1133
|
+
height: diagonal + 'px',
|
|
1134
|
+
opacity: 1
|
|
1135
|
+
}, {
|
|
1136
|
+
duration: 400,
|
|
1137
|
+
easing: 'ease-out',
|
|
1138
|
+
display: 'block',
|
|
1139
|
+
queue: NO
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
let UUID = BMUUIDMake();
|
|
1143
|
+
|
|
1144
|
+
$(window).on(endEvent + UUID, function (event) {
|
|
1145
|
+
$(window).off(endEvent + UUID);
|
|
1146
|
+
ripple.velocity({
|
|
1147
|
+
opacity: 0
|
|
1148
|
+
}, {
|
|
1149
|
+
duration: 300,
|
|
1150
|
+
easing: 'ease-in-out',
|
|
1151
|
+
display: 'none',
|
|
1152
|
+
queue: NO
|
|
1153
|
+
});
|
|
1154
|
+
});
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Creates a material design-like ripple that activates on mouse/touch down and up events on the given target element.
|
|
1160
|
+
* @param target <$> The jQuery element.
|
|
1161
|
+
* {
|
|
1162
|
+
* @param withColor <BMColor or String, nullable> Defaults to rgba(0, 0, 0, .1). The color to use for the ripple.
|
|
1163
|
+
* }
|
|
1164
|
+
* @return <$> The newly created ripple element.
|
|
1165
|
+
*/
|
|
1166
|
+
export function BMRippleMakeForTarget(target, args) {
|
|
1167
|
+
// TODO: get rid of jQuery
|
|
1168
|
+
var color = (args && args.withColor) || 'rgba(0, 0, 0, .1)';
|
|
1169
|
+
|
|
1170
|
+
color = color.RGBAString || color;
|
|
1171
|
+
|
|
1172
|
+
var ripple = $('<div style="position: absolute; display: block; top: 0px; left: 0px; border-radius: 50%;"></div>');
|
|
1173
|
+
ripple.css({backgroundColor: color});
|
|
1174
|
+
|
|
1175
|
+
BMRippleMakeWithElement(ripple, {forTarget: target});
|
|
1176
|
+
|
|
1177
|
+
return ripple;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
// @endtype
|