redshift
Version:
A JavaScript UX framework. Handles animation, UI physics and user input tracking.
1,873 lines (1,449 loc) • 148 kB
JavaScript
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/*!****************************!*\
!*** ./src/load/global.js ***!
\****************************/
/***/ function(module, exports, __webpack_require__) {
window.redshift = __webpack_require__(/*! ./module.js */ 1);
/***/ },
/* 1 */
/*!****************************!*\
!*** ./src/load/module.js ***!
\****************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
var redshift = __webpack_require__(/*! ../redshift.js */ 2);
redshift
// Add default Rubix processor modules
.addRubix('fire', __webpack_require__(/*! ../rubix/fire.js */ 3))
.addRubix('link', __webpack_require__(/*! ../rubix/link.js */ 4))
.addRubix('play', __webpack_require__(/*! ../rubix/play.js */ 5))
.addRubix('run', __webpack_require__(/*! ../rubix/run.js */ 6))
.addRubix('seek', __webpack_require__(/*! ../rubix/seek.js */ 7))
.addRubix('track', __webpack_require__(/*! ../rubix/track.js */ 8))
// Add DOM value routes
.addRoute('values', __webpack_require__(/*! ../routes/values.js */ 9))
.addRoute('css', __webpack_require__(/*! ../routes/css.js */ 10))
.addRoute('attr', __webpack_require__(/*! ../routes/attr.js */ 11))
.addRoute('path', __webpack_require__(/*! ../routes/path.js */ 12));
module.exports = redshift;
/***/ },
/* 2 */
/*!*************************!*\
!*** ./src/redshift.js ***!
\*************************/
/***/ function(module, exports, __webpack_require__) {
/*
Redshift core object
Exposes methods to create new classes and define new modules
*/
"use strict";
var Action = __webpack_require__(/*! ./action/action.js */ 13),
ActionGroup = __webpack_require__(/*! ./action-group/action-group.js */ 14),
domGroup = __webpack_require__(/*! ./action-group/dom.js */ 15),
Input = __webpack_require__(/*! ./input/input.js */ 16),
Process = __webpack_require__(/*! ./process/process.js */ 17),
presets = __webpack_require__(/*! ./action/presets.js */ 18),
easing = __webpack_require__(/*! ./utils/easing.js */ 19),
calc = __webpack_require__(/*! ./utils/calc.js */ 20),
utils = __webpack_require__(/*! ./utils/utils.js */ 21),
route = __webpack_require__(/*! ./action/routes.js */ 22),
registerRubix = __webpack_require__(/*! ./core/register-rubix.js */ 23),
registerSimulation = __webpack_require__(/*! ./core/register-simulation.js */ 24);
module.exports = {
/*
Create a new Action controller
@return [Action]: Newly-created Action
*/
newAction: function (props) {
return (utils.isArray(props)) ? new ActionGroup(props) : new Action(props);
},
/*
Create a new Input controller
@return [Input]: Newly-created Input
*/
newInput: function () {
return new Input(arguments[0], arguments[1]);
},
/*
Create a new process
@param [function]: Function to run every frame
@param: Scope
@return [Process]
*/
newProcess: function () {
return new Process(arguments[0], arguments[1]);
},
/*
Create an Action Group prepopulated with DOM properties
@param [string || NodeList || jQuery]: Selector, nodeList or jQuery selection
*/
dom: function (selector) {
return domGroup(selector);
},
/*
Define a new Action preset
Syntax
.definePreset(name, preset)
@param [string]: Name of preset
@param [object]: Preset options/properties
.definePreset(presets)
@param [object]: Multiple presets as named object
@return [Redshift]
*/
addPreset: function () {
presets.add.apply(presets, arguments);
return this;
},
/*
Add bezier curve function
Add the specified bezier curve the EasingFunction's available easings
My favourite bezier curve generator is Lea Verou's excellent http://cubic-bezier.com/
@param [string]: Name of the new easing function
@params [number]: x/y coordinates of handles
*/
addBezier: function () {
easing.add.apply(easing, arguments);
return this;
},
/*
Add value route
The default values object is .values, however any provided object
will be parsed into values and given a .route property that is the name of
its original object. For instance providing
example: {
test: 20
}
will be parsed into
values: {
test: {
current: 20,
route: 'example'
}
}
If we provide a custom route with this name, we can custom-parse values
on the way in, and also on the way out.
*/
addRoute: function () {
route.add.apply(route, arguments);
return this;
},
/*
Add simulation
@param [string]: Simulation name
@param [function]: Method to calculate new velocity
*/
addSimulation: function () {
registerSimulation.apply(this, arguments);
return this;
},
/*
Add Rubix
@param [string]: Rubix name
@param [object]: Methods and properties
*/
addRubix: function () {
registerRubix.apply(this, arguments);
return this;
},
// Expose calc and utils modules
calc: calc,
utils: utils
};
/***/ },
/* 3 */
/*!***************************!*\
!*** ./src/rubix/fire.js ***!
\***************************/
/***/ function(module, exports, __webpack_require__) {
/*
Return current value and immedietly end
*/
"use strict";
module.exports = {
/*
Process new value
Return existing current
@param [string]: Name of value
@param [Value]: Current value
*/
process: function (key, value) {
return value.current;
},
/*
Has Action ended?
Returns true to end immedietly
@return [boolean]: true
*/
hasEnded: function () {
return true;
}
};
/***/ },
/* 4 */
/*!***************************!*\
!*** ./src/rubix/link.js ***!
\***************************/
/***/ function(module, exports, __webpack_require__) {
/*
Link the calculations of on Value into the output of another.
Activate by setting the link property of one value with the name
of either an Input property or another Value.
Map the linked value with mapLink and provide a corressponding mapTo
array to translate values from one into the other. For instance:
{
link: 'x',
mapLink: [0, 100, 200],
mapTo: [-100, 0, -100]
}
An output value of 50 from 'x' will translate to -50 for this Value
*/
"use strict";
var calc = __webpack_require__(/*! ../utils/calc.js */ 20),
STRING = 'string',
/*
Translate our mapLink value into mapTo
@param [number]: Calculated value from linked value
@param [Value || object]: Linked value or empty object if we're linking to input
@param [array]: List of numbers relating to linked value
@param [array]: List of numbers relating to this value
*/
findMappedValue = function (newValue, linkedValue, toValue, mapLink, mapTo) {
var mapLength = mapLink.length,
i = 1,
lastLinkValue,
thisLinkValue,
lastToValue,
thisToValue;
for (; i < mapLength; i++) {
// Assign values from array, or if they're strings, look for them in linkedValue
lastLinkValue = (typeof mapLink[i - 1] === STRING) ? linkedValue[mapLink[i - 1]] : mapLink[i - 1];
thisLinkValue = (typeof mapLink[i] === STRING) ? linkedValue[mapLink[i]] : mapLink[i];
lastToValue = (typeof mapTo[i - 1] === STRING) ? toValue[mapTo[i - 1]] : mapTo[i - 1];
thisToValue = (typeof mapTo[i] === STRING) ? toValue[mapTo[i]] : mapTo[i];
// Check if we've gone past our calculated value, or if we're at the end of the array
if (newValue < thisLinkValue || i === mapLength - 1) {
newValue = calc.value(calc.restricted(calc.progress(newValue, lastLinkValue, thisLinkValue), 0, 1), lastToValue, thisToValue);
break;
}
}
return newValue;
};
module.exports = {
surpressMethod: true,
/*
Process this value
First check if this value exists as a Value, if not
check within Input (if we have one)
@param [string]: Key of current value
@param [Value]: Current value
@param [object]: Collection of all Action values
@param [object]: Action properties
@param [Action]: Current Action
@return [number]: Calculated value
*/
process: function (key, value, values, action) {
var newValue = value.current,
linkKey = value.link,
linkedValue = values[linkKey] ? values[linkKey] : {},
inputOffset = action.inputOffset;
// Then check values in Input
if (inputOffset && inputOffset.hasOwnProperty(linkKey)) {
newValue = value.origin + (inputOffset[linkKey] * value.amp);
// First look at Action and check value isn't linking itself
} else if (linkedValue.current !== undefined && key !== linkKey) {
newValue = linkedValue.current;
}
// If we have mapLink and mapTo properties, translate the new value
if (value.mapLink && value.mapTo) {
newValue = findMappedValue(newValue, linkedValue, value, value.mapLink, value.mapTo);
}
return newValue;
},
limit: function (output, value) {
return calc.restricted(output, value.min, value.max);
}
};
/***/ },
/* 5 */
/*!***************************!*\
!*** ./src/rubix/play.js ***!
\***************************/
/***/ function(module, exports, __webpack_require__) {
/*
Play rubix
Translate numbers for a set amount of time, applying easing if defined
*/
"use strict";
var calc = __webpack_require__(/*! ../utils/calc.js */ 20),
easing = __webpack_require__(/*! ../utils/easing.js */ 19),
utils = __webpack_require__(/*! ../utils/utils.js */ 21),
CURRENT = 'current',
HAS_ENDED = 'hasEnded';
module.exports = {
/*
Update Action elapsed time
@param [Action]
@param [object]: Action properties
@param [number]: Timestamp of current frame
*/
updateInput: function (action, frameDuration) {
action.elapsed += (frameDuration * action.dilate) * action.playDirection;
action[HAS_ENDED] = true;
},
/*
Calculate progress of value based on time elapsed,
value delay/duration/stagger properties
@param [string]: Key of current value
@param [Value]: Current value
@param [object]: Collection of all Action values
@param [object]: Action properties
@param [Action]: Current Action
@param [number]: Duration of frame in ms
@return [number]: Calculated value
*/
process: function (key, value, values, action) {
var target = value.to,
newValue = value[CURRENT],
progress, progressTarget;
// If we have a target, process
if (target !== undefined) {
progress = calc.restricted(calc.progress(action.elapsed - value.delay, value.duration) - value.stagger, 0, 1);
progressTarget = (action.playDirection === 1) ? 1 : 0;
// Mark Action as not ended if still in progress
if (progress !== progressTarget) {
action[HAS_ENDED] = false;
// Or clear value target
} else {
value.to = undefined;
}
// Step progress if we're stepping
if (value.steps) {
progress = utils.stepProgress(progress, value.steps, value.stepDirection);
}
// Ease value with progress
newValue = easing.withinRange(progress, value.origin, target, value.ease);
}
return newValue;
},
/*
Return hasEnded property
@param [boolean]: Have all Values hit 1 progress?
*/
hasEnded: function (action) {
return action[HAS_ENDED];
}
};
/***/ },
/* 6 */
/*!**************************!*\
!*** ./src/rubix/run.js ***!
\**************************/
/***/ function(module, exports, __webpack_require__) {
/*
Run physics simulation
*/
"use strict";
var calc = __webpack_require__(/*! ../utils/calc.js */ 20),
simulate = __webpack_require__(/*! ../action/simulate.js */ 25);
module.exports = {
// [boolean]: Tell Redshift this rubix calculates a new velocity itself
calculatesVelocity: true,
/*
Simulate the Value's per-frame movement
@param [string]: Key of current value
@param [Value]: Current value
@param [object]: Collection of all Action values
@param [object]: Action properties
@param [Action]: Current Action
@param [number]: Duration of frame in ms
@return [number]: Calculated value
*/
process: function (key, value, values, action, frameDuration) {
value.velocity = simulate(value.simulate, value, frameDuration, action.started);
return value.current + calc.speedPerFrame(value.velocity, frameDuration);
},
/*
Has this action ended?
Use a framecounter to see if Action has changed in the last x frames
and declare ended if not
@param [Action]
@param [boolean]: Has Action changed?
@return [boolean]: Has Action ended?
*/
hasEnded: function (action, hasChanged) {
action.inactiveFrames = hasChanged ? 0 : action.inactiveFrames + 1;
return (action.inactiveFrames > action.maxInactiveFrames);
},
/*
Limit output to value range, if any
If velocity is at or more than range, and value has a bounce property,
run the bounce simulation
@param [number]: Calculated output
@param [Value]: Current Value
@return [number]: Limit-adjusted output
*/
limit: function (output, value) {
var isOutsideMax = (output >= value.max),
isOutsideMin = (output <= value.min),
isOutsideRange = isOutsideMax || isOutsideMin;
if (isOutsideRange) {
output = calc.restricted(output, value.min, value.max);
if (value.bounce) {
value.velocity = simulate('bounce', value);
} else if (value.capture) {
simulate('capture', value, isOutsideMax ? value.max : value.min);
}
}
return output;
}
};
/***/ },
/* 7 */
/*!***************************!*\
!*** ./src/rubix/seek.js ***!
\***************************/
/***/ function(module, exports, __webpack_require__) {
/*
Return current value and immedietly end
*/
"use strict";
var playRubix = __webpack_require__(/*! ./play.js */ 5);
module.exports = {
/*
Process new value
Return existing current
@param [string]: Name of value
@param [Value]: Current value
*/
process: playRubix.process,
/*
Has Action ended?
Returns true to end animation, and sets rubix to 'play'
@return [boolean]: true
*/
hasEnded: function (action) {
action.rubix = 'play';
return true;
}
};
/***/ },
/* 8 */
/*!****************************!*\
!*** ./src/rubix/track.js ***!
\****************************/
/***/ function(module, exports, __webpack_require__) {
/*
Track user input
*/
"use strict";
var calc = __webpack_require__(/*! ../utils/calc.js */ 20),
CURRENT = 'current',
INPUT_OFFSET = 'inputOffset';
module.exports = {
/*
Update Input
@param [Action]
@param [object]: Action properties
*/
updateInput: function (action) {
action[INPUT_OFFSET] = calc.offset(action.inputOrigin, action.input[CURRENT]);
},
/*
Move Value relative to Input movement
@param [string]: Key of current value
@param [Value]: Current value
@param [object]: Collection of all Action values
@param [object]: Action properties
@param [Action]: Current Action
@return [number]: Calculated value
*/
process: function (key, value, values, action) {
return (action[INPUT_OFFSET].hasOwnProperty(key)) ? value.origin + action[INPUT_OFFSET][key] : value[CURRENT];
},
/*
Has this Action ended?
@return [boolean]: False to make user manually finish .track()
*/
hasEnded: function () {
return false;
}
};
/***/ },
/* 9 */
/*!******************************!*\
!*** ./src/routes/values.js ***!
\******************************/
/***/ function(module, exports, __webpack_require__) {
/*
Values route (Redshift default)
Handles raw values and outputs to user-defined callbacks
*/
"use strict";
var fireCallback = function (name, bucket, action) {
if (action[name]) {
action[name].call(action.scope, bucket);
}
};
module.exports = {
makeDefault: true,
onStart: function (bucket, action) {
if (action.onStart) {
action.onStart.call(action.scope);
}
},
onFrame: function (bucket, action, values) {
fireCallback('onFrame', bucket, action, values);
},
onChange: function (bucket, action, values) {
fireCallback('onChange', bucket, action, values);
},
onEnd: function (bucket, action, values) {
fireCallback('onEnd', bucket, action, values);
}
};
/***/ },
/* 10 */
/*!***************************!*\
!*** ./src/routes/css.js ***!
\***************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
var build = __webpack_require__(/*! ./css/build.js */ 26),
split = __webpack_require__(/*! ./css/split.js */ 27),
css = 'css',
cssOrder = css + 'Order',
cssCache = css + 'Cache';
module.exports = {
preprocess: function (key, value, action) {
var values = split(key, value, action);
action.updateOrder(key, false, cssOrder);
return values;
},
onChange: function (output, action, values) {
action[cssCache] = action[cssCache] || {};
action.style(build(output, action[cssOrder], action[cssCache], values));
}
};
/***/ },
/* 11 */
/*!****************************!*\
!*** ./src/routes/attr.js ***!
\****************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
module.exports = {
onChange: function (output, action) {
var dom = action.dom;
if (dom) {
for (var key in output) {
dom.setAttribute(key, output[key]);
}
}
}
};
/***/ },
/* 12 */
/*!****************************!*\
!*** ./src/routes/path.js ***!
\****************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
var createStyles = __webpack_require__(/*! ./path/builder.js */ 28);
module.exports = {
onStart: function (bucket, action) {
if (action.dom) {
action.pathLength = action.dom.getTotalLength();
}
},
onChange: function (output, action) {
action.style(createStyles(output, action.pathLength));
}
};
/***/ },
/* 13 */
/*!******************************!*\
!*** ./src/action/action.js ***!
\******************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
var parseArgs = __webpack_require__(/*! ./parse-args.js */ 29),
Value = __webpack_require__(/*! ../types/value.js */ 30),
Queue = __webpack_require__(/*! ./queue.js */ 31),
Process = __webpack_require__(/*! ../process/process.js */ 17),
processor = __webpack_require__(/*! ./processor.js */ 32),
routes = __webpack_require__(/*! ./routes.js */ 22),
defaultProps = __webpack_require__(/*! ../defaults/action-props.js */ 33),
defaultState = __webpack_require__(/*! ../defaults/action-state.js */ 34),
utils = __webpack_require__(/*! ../utils/utils.js */ 21),
styler = __webpack_require__(/*! ../routes/css/styler.js */ 35),
namespace = function (key, space) {
return (space && space !== routes.defaultRoute) ? key + '.' + space : key;
},
Action = function () {
var self = this;
// Create value repo
self.values = {};
this.scope = this;
self.setProp(defaultState);
self.resetProps();
// Register process wth cycl
self.process = new Process(function (framestamp, frameDuration) {
if (self.isActive()) {
processor(self, framestamp, frameDuration);
}
});
self.queue = new Queue();
self.output = {};
self.set(parseArgs.generic.apply(self, arguments));
};
Action.prototype = {
/*
Play the provided actions as animations
Syntax
.play(playlist, [override])
@param [string]: Playlist of presets
@param [object]: (optional) Override object
.play(params)
@param [object]: Action properties
.play(params, [duration, easing, onEnd])
@param [object]: Action props
@param [number]: Duration in ms
@param [string]: Easing function to apply
@param [function]: Function to run on end
@return [Action]
*/
play: function () {
var props = parseArgs.play.apply(this, arguments);
if (!this.isActive()) {
this.set(props, 'to');
this.start('play');
} else {
this.queue.add.apply(this.queue, arguments);
}
return this;
},
/*
Set Action values and properties
Syntax
.set(params)
@param [object]: Action properties
@return [Action]
*/
set: function (props, defaultProp) {
var self = this;
// Reset properties to defaults
this.resetProps();
// Remove current values from order list
this.clearOrder();
// Update current properties
this.setProp(props);
// Set default property to current if it isn't set
defaultProp = defaultProp || 'current';
// Loop over values and update
routes.shard(function (route, routeValues) {
var preprocessedValues = {},
valueBase = {},
value,
base = {
route: route.name
};
for (var key in routeValues) {
if (routeValues.hasOwnProperty(key)) {
value = routeValues[key];
if (!utils.isObj(value)) {
valueBase = { name: key };
valueBase[defaultProp] = value;
} else {
valueBase = value;
valueBase.name = key;
}
valueBase = utils.merge(base, valueBase);
// If no preprocess step, assign directly
if (!route.preprocess) {
self.setValue(key, valueBase, props, route.name, true);
// Else preprocess and add each returned value
} else {
preprocessedValues = route.preprocess(key, valueBase, self, props);
for (var subKey in preprocessedValues) {
self.setValue(subKey, preprocessedValues[subKey], props, route.name, true);
}
}
}
}
}, props);
self.resetOrigins();
return self;
},
/*
Loop through all values and create origin points
*/
resetOrigins: function () {
var values = this.values,
key = '';
for (key in values) {
if (values.hasOwnProperty(key)) {
values[key].origin = values[key].current;
}
}
},
/*
Start Action
@param [string]: Name of processing type to use
@return [Action]
*/
start: function (processType) {
var input = this.input;
this.resetProgress();
if (processType) {
this.rubix = processType;
}
if (processType !== 'track' && input && input.stop) {
input.stop();
}
this.activate();
return this;
},
/*
Stop current Action process
*/
stop: function () {
this.queue.clear();
this.pause();
return this;
},
/*
Pause current Action
*/
pause: function () {
var self = this,
input = this.input;
self.isActive(false);
self.process.stop();
if (input && input.stop) {
input.stop();
}
return self;
},
/*
Move playhead to a specific location
@param [number]: 0-1
*/
seek: function (seekTo) {
this.elapsed = this.duration * seekTo;
if (!this.isActive()) {
this.rubix = 'seek';
this.activate();
}
return this;
},
activate: function () {
this.isActive(true);
this.started = utils.currentTime() + this.delay;
this.framestamp = this.started;
this.firstFrame = true;
this.process.start();
},
/*
Resume a paused Action
*/
resume: function () {
var self = this;
self.started = utils.currentTime();
self.framestamp = self.started;
self.isActive(true);
self.process.start();
return self;
},
/*
Reset Action progress and values
*/
reset: function () {
var self = this,
values = self.values;
self.resetProgress();
for (var key in values) {
values[key].reset();
}
return self;
},
/*
Reset Action progress
*/
resetProgress: function () {
this.elapsed = (this.playDirection === 1) ? 0 : this.duration;
this.started = utils.currentTime();
return this;
},
/*
Reverse Action progress and values
*/
reverse: function () {
var values = this.values;
this.playDirection = this.playDirection * -1;
for (var key in values) {
if (values.hasOwnProperty(key)) {
values[key].retarget();
}
}
return this;
},
/*
Swap value origins and to
*/
flipValues: function () {
var values = this.values;
this.elapsed = this.duration - this.elapsed;
for (var key in values) {
values[key].flip();
}
return this;
},
toggle: function () {
if (this.isActive()) {
this.pause();
} else {
this.resume();
}
return this;
},
/*
Check for next steps and perform, stop if not
*/
next: function () {
var self = this,
nexts = [{
key: 'loop',
callback: self.reset
}, {
key: 'yoyo',
callback: self.reverse
}, {
key: 'flip',
callback: self.flipValues
}],
possibles = nexts.length,
hasNext = false;
for (var i = 0; i < possibles; ++i) {
if (self.checkNextStep(nexts[i].key, nexts[i].callback)) {
hasNext = true;
break;
}
}
if (!hasNext && !self.playNext()) {
self.stop();
} else {
self.isActive(true);
}
return self;
},
/*
Check next step
@param [string]: Name of step ('yoyo' or 'loop')
@param [callback]: Function to run if we take this step
*/
checkNextStep: function (key, callback) {
var COUNT = 'Count',
stepTaken = false,
step = this[key],
count = this[key + COUNT],
forever = (step === true);
if (forever || utils.isNum(step)) {
++count;
this[key + COUNT] = count;
if (forever || count <= step) {
callback.call(this);
stepTaken = true;
}
}
return stepTaken;
},
/*
Next in playlist
*/
playNext: function () {
var stepTaken = false,
nextInQueue = this.queue.next(this.playDirection);
if (utils.isArray(nextInQueue)) {
this.set(parseArgs.generic.apply(this, nextInQueue), 'to')
.reset();
stepTaken = true;
}
return stepTaken;
},
setValue: function (key, value, inherit, space, reset) {
var existing = this.getValue(key, space);
key = namespace(key, space);
// Update if value exists
if (existing) {
// Overwrite with defaults
if (reset) {
existing.resetProps();
}
existing.set(value, inherit);
// Or create new if it doesn't
} else {
this.values[key] = new Value(key, value, inherit, this);
}
return this;
},
getValue: function (key, space) {
key = namespace(key, space);
return this.values[key];
},
setProp: function (data, prop) {
var multiArg = (arguments.length > 1),
defaultRoute = routes.getName(),
toSet = multiArg ? {} : data,
key = '';
// If this is a key/value setter, add to toSet
if (multiArg) {
toSet[data] = prop;
}
// Loop over toSet and assign to our data store
for (key in toSet) {
if (toSet.hasOwnProperty(key) && key != defaultRoute) {
this[key] = toSet[key];
}
}
return this;
},
resetProps: function () {
this.setProp(defaultProps);
return this;
},
/*
Is Action active?
@param [boolean] (optional): If provided, will set action to active/inactive
@return [boolean]: Active status
*/
isActive: function (active) {
var isActive = (active !== undefined) ? active : this.active;
if (active === true) {
this.hasChanged = active;
}
this.active = isActive;
return isActive;
},
/*
Update order of value keys
@param [string]: Key of value
@param [boolean]: Whether to move value to back
@param [string] (optional): Name of order array (if not default)
*/
updateOrder: function (key, moveToBack, orderName) {
var pos, order;
orderName = orderName || 'order';
order = this[orderName] = this[orderName] || [];
pos = order.indexOf(key);
if (pos === -1 || moveToBack) {
order.push(key);
if (pos !== -1) {
order.splice(pos, 1);
}
}
},
clearOrder: function () {
this.order = [];
},
/*
Style our dom element
Becomes get if props is string, set if object
*/
style: function (name, props) {
var elementIsDefined = (arguments.length === 2),
dom,
returnVal;
props = elementIsDefined ? props : name;
name = elementIsDefined ? name : 'dom';
dom = this[name];
if (dom) {
returnVal = styler(dom, props);
}
return (returnVal === false) ? this : returnVal;
}
};
module.exports = Action;
/***/ },
/* 14 */
/*!******************************************!*\
!*** ./src/action-group/action-group.js ***!
\******************************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
var Action = __webpack_require__(/*! ../action/action.js */ 13),
generateMethodIterator = __webpack_require__(/*! ./generate-iterator.js */ 36),
defaultDuration = 250,
defaultEase = 'linear',
/*
Action group constructor
*/
ActionGroup = function (actions) {
this.actions = actions || [];
},
actionGroupPrototype = ActionGroup.prototype;
/*
Stagger the execution of the provided Action method
@param [string]: Name of Action method to call
@param [number] (optional): Duration between method calls
@param [string || object] (optional): Argument to pass method
@param [string] (optional): Easing
*/
actionGroupPrototype.stagger = function (method, duration, props, ease) {
var self = this,
numActions = this.actions.length,
i = -1;
this._stagger = this._stagger || new Action();
duration = duration || defaultDuration;
ease = ease || defaultEase;
this._stagger.stop().play({
values: {
i: {
current: i,
to: numActions - 1
}
},
round: true,
onChange: function (output) {
var newIndex = output.i;
// If our new index is only one more than the last
if (newIndex === i + 1) {
self.actions[newIndex][method](props);
// Or it's more than one more than the last, so fire all indecies
} else {
for (var index = i + 1; index <= newIndex; index++) {
self.actions[index][method](props);
}
}
i = newIndex;
}
}, duration * numActions, ease);
return this;
};
/*
Add a new Action to the group
@param [object]: Action properties
*/
actionGroupPrototype.add = function (props) {
this.actions.push(new Action(props));
};
// Initialise Action Group methods
(function () {
for (var method in Action.prototype) {
actionGroupPrototype[method] = generateMethodIterator(method);
}
})();
module.exports = ActionGroup;
/***/ },
/* 15 */
/*!*********************************!*\
!*** ./src/action-group/dom.js ***!
\*********************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
var ActionGroup = __webpack_require__(/*! ./action-group.js */ 14);
module.exports = function (selector) {
var actionGroup = new ActionGroup(),
elements = [],
numElements = 0,
i = 0,
domSelection = (typeof selector === 'string') ? document.querySelectorAll(selector) : selector;
// if jQuery selection, get Array
if (domSelection.get) {
elements = domSelection.get();
// Or convert NodeList to Array
} else if (domSelection.length) {
elements = [].slice.call(domSelection);
// Or put Element into array
} else {
elements.push(domSelection);
}
numElements = elements.length;
for (; i < numElements; i++) {
actionGroup.add({
dom: elements[i]
});
}
return actionGroup;
};
/***/ },
/* 16 */
/*!****************************!*\
!*** ./src/input/input.js ***!
\****************************/
/***/ function(module, exports, __webpack_require__) {
/*
Input controller
*/
"use strict";
var calc = __webpack_require__(/*! ../utils/calc.js */ 20),
utils = __webpack_require__(/*! ../utils/utils.js */ 21),
History = __webpack_require__(/*! ../utils/history.js */ 37),
/*
Input constructor
Syntax
newInput(name, value[, poll])
@param [string]: Name of to track
@param [number]: Initial value
@param [function] (optional): Function to poll Input data
newInput(props[, poll])
@param [object]: Object of values
@param [function] (optional): Function to poll Input data
@return [Input]
*/
Input = function () {
var pollPos = arguments.length - 1;
this.current = {};
this.offset = {};
this.velocity = {};
this.history = new History();
this.update(arguments[0], arguments[1]);
if (utils.isFunc(arguments[pollPos])) {
this.poll = arguments[pollPos];
}
};
Input.prototype = {
// [number]: Number of frames of inactivity before velocity is turned to 0
maxInactiveFrames: 2,
// [number]: Number of frames input hasn't been updated
inactiveFrames: 0,
/*
Get latest input values
@param [string] (optional): Name of specific property to return
@return [object || number]: Latest input values or, if specified, single value
*/
get: function (prop) {
var latest = this.history.get(),
val = (prop !== undefined) ? latest[prop] : latest;
return val;
},
/*
Update the input values
Syntax
input.update(name, value)
@param [string]: Name of to track
@param [number]: Initial value
input.update(props)
@param [object]: Object of values
@return [Input]
*/
update: function (arg0, arg1) {
var values = {};
if (utils.isNum(arg1)) {
values[arg0] = arg1;
} else {
values = arg0;
}
this.history.add(utils.merge(this.current, values));
return this;
},
/*
Check for input movement and update pointer object's properties
@param [number]: Timestamp of frame
@return [Input]
*/
onFrame: function (timestamp) {
var latest, hasChanged;
// Check provided timestamp against lastFrame timestamp and return input has already been updated
if (timestamp === this.lastFrame) {
return;
}
latest = (this.poll) ? this.poll() : this.history.get();
hasChanged = utils.hasChanged(this.current, latest);
// If input has changed between frames
if (hasChanged) {
this.velocity = calc.offset(this.current, latest);
this.current = latest;
this.inactiveFrames = 0;
// Or it hasn't moved and our frame limit has been reached
} else if (this.inactiveFrames >= this.maxInactiveFrames) {
this.velocity = calc.offset(this.current, this.current);
// Or input hasn't changed
} else {
this.inactiveFrames++;
}
this.lastFrame = timestamp;
return this;
}
};
module.exports = Input;
/***/ },
/* 17 */
/*!********************************!*\
!*** ./src/process/process.js ***!
\********************************/
/***/ function(module, exports, __webpack_require__) {
/*
Process
*/
"use strict";
var manager = __webpack_require__(/*! ./manager.js */ 38),
/*
Process constructor
Syntax
var process = new Process(scope, callback);
var process = new Process(callback);
*/
Process = function (arg0, arg1) {
var hasScope = (arg1 !== undefined),
callback = hasScope ? arg1 : arg0,
scope = hasScope ? arg0 : this;
this.setCallback(callback);
this.setScope(scope);
this.setId(manager.register(this));
};
Process.prototype = {
/*
[boolean]: Is this process currently active?
*/
isActive: false,
/*
[boolean]: Has this process been killed?
*/
isKilled: false,
/*
Fire callback
@param [timestamp]: Timestamp of currently-executed frame
@param [number]: Time since last frame
*/
fire: function (timestamp, elapsed) {
// Check timers
if (this.isActive) {
this.callback.call(this.scope, timestamp, elapsed);
}
if (this.isInterval) {
this.deactivate();
}
return this;
},
/*
Set process callback
@param [function]: Function to fire per frame
@return [this]
*/
setCallback: function (callback) {
this.callback = callback;
return this;
},
/*
Set callback scope
@param [function]: Fire callback in this context
@return [this]
*/
setScope: function (scope) {
this.scope = scope;
return this;
},
/*
Start process
@param [int]: Duration of process in ms, 0 if indefinite
@return [this]
*/
start: function (duration) {
var self = this;
this.reset();
this.activate();
if (duration) {
this.stopTimer = setTimeout(function () {
self.stop();
}, duration);
this.isStopTimerActive = true;
}
return this;
},
/*
Stop process
@return [this]
*/
stop: function () {
this.reset();
this.deactivate();
return this;
},
/*
Activate process
@return [this]
*/
activate: function () {
if (!this.isKilled) {
this.isActive = true;
manager.activate(this.id);
}
return this;
},
/*
Deactivate process
@return [this]
*/
deactivate: function () {
this.isActive = false;
manager.deactivate(this.id);
return this;
},
/*
Fire process every x ms
@param [int]: Number of ms to wait between refiring process.
@return [this]
*/
every: function (interval) {
var self = this;
this.reset();
this.isInterval = true;
this.intervalTimer = setInterval(function () {
self.activate();
}, interval);
this.isIntervalTimeActive = true;
return this;
},
/*
Clear all timers
@param
*/
reset: function () {
this.isInterval = false;
if (this.isStopTimerActive) {
clearTimeout(this.stopTimer);
}
if (this.isIntervalTimeActive) {
clearInterval(this.intervalTimer);
}
return this;
},
/*
Kill function in manager, release for garbage collection
*/
kill: function () {
this.stop();
this.isKilled = true;
manager.kill(this.id);
},
setId: function (id) {
this.id = id;
return this;
}
};
module.exports = Process;
/***/ },
/* 18 */
/*!*******************************!*\
!*** ./src/action/presets.js ***!
\*******************************/
/***/ function(module, exports, __webpack_require__) {
"use strict";
var utils = __webpack_require__(/*! ../utils/utils.js */ 21),
generateKeys = function (key) {
var keys = key.split(DOT),
keysLength = keys.length,
lastKey = keys[0],
i = 1;
for (; i < keysLength; i++) {
keys[i] = lastKey += DOT + keys[i];
}
return keys;
},
presetStore = {},
DOT = '.',
Presets = function () {};
Presets.prototype = {
/*
Define a new Action preset
Syntax
.define(name, preset)
@param [string]: Name of preset
@param [object]: Preset options/properties
.define(presets)
@param [object]: Multiple presets as named object
@return [Redshift]
*/
add: function (name, preset) {
var presets = {},
key = '';
if (utils.isObj(name)) {
presets = name;
} else {
presets[name] = preset;
}
for (key in presets) {
if (presets.hasOwnProperty(key)) {
presetStore[key] = presets[key];
}
}
},
/*
Get defined action
@param [string]: The name of the predefined action
*/
getDefined: function (key) {
var props = {},
thisProp = {},
keys = generateKeys(key),
keysLength = keys.length,
i = 0;
for (; i < keysLength; i++) {
thisProp = presetStore[keys[i]];
if (thisProp) {
props = utils.merge(props, thisProp);
}
}
return props;
}
};
module.exports = new Presets();
/***/ },
/* 19 */
/*!*****************************!*\
!*** ./src/utils/easing.js ***!
\*****************************/
/***/ function(module, exports, __webpack_require__) {
/*
Easing functions
----------------------------------------
Generates and provides easing functions based on baseFunction definitions
A call to easingFunction.get('functionName') returns a function that can be passed:
@param [number]: Progress 0-1
@param [number] (optional): Amp modifier, only accepted in some easing functions
and is used to adjust overall strength
@return [number]: Eased progress
We can gener