diff --git a/README.md b/README.md
index fc7269cb39c1c..a9205efe722d3 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ It offers the following functionality through built-in components:
* Track and control [WeMo switches](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/)
* Track and control [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast)
* Track running services by monitoring `ps` output
+ * Track and control [Tellstick devices and sensors](http://www.telldus.se/products/tellstick)
* Turn on the lights when people get home after sun set
* Turn on lights slowly during sun set to compensate for light loss
* Turn off all lights and devices when everybody leaves the house
@@ -51,7 +52,7 @@ docker run -d --name="home-assistant" -v /path/to/homeassistant/config:/config -
After you got the demo mode running it is time to enable some real components and get started. An example configuration file has been provided in [/config/home-assistant.conf.example](https://github.com/balloob/home-assistant/blob/master/config/home-assistant.conf.example).
### Philips Hue
-To get Philips Hue working you will have to connect Home Assistant to the Hue bridge.
+To get Philips Hue working you will have to connect Home Assistant to the Hue bridge.
Run the following command from your config dir and follow the instructions:
@@ -146,11 +147,11 @@ When a state is changed a state_changed event is fired for which the device_sun_
In the event that the state of device 'Paulus Nexus 5' changes to the 'Home' state:
If the sun has set and the lights are not on:
Turn on the lights
-
+
In the event that the combined state of all tracked devices changes to 'Not Home':
If the lights are on:
Turn off the lights
-
+
In the event of the sun setting:
If the lights are off and the combined state of all tracked device equals 'Home':
Turn on the lights
@@ -167,7 +168,7 @@ Action: maintains state of `weather.sun` including attributes `next_rising` and
**device_tracker**
Keeps track of which devices are currently home.
-Action: sets the state per device and maintains a combined state called `all_devices`. Keeps track of known devices in the file `config/known_devices.csv`.
+Action: sets the state per device and maintains a combined state called `all_devices`. Keeps track of known devices in the file `config/known_devices.csv`.
**light**
Keeps track which lights are turned on and can control the lights. It has [4 built-in light profiles](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/light/light_profiles.csv) which you're able to extend by putting a light_profiles.csv file in your config dir.
@@ -195,8 +196,8 @@ Turns lights on or off using a light control component based on state of the sun
Depends on: light control, track_sun, device_tracker
Action:
- * Turns lights off when all devices leave home.
- * Turns lights on when a device is home while sun is setting.
+ * Turns lights off when all devices leave home.
+ * Turns lights on when a device is home while sun is setting.
* Turns lights on when a device gets home after sun set.
**chromecast**
@@ -218,10 +219,13 @@ Registers service `downloader/download_file` that will download files. File to d
**browser**
Registers service `browser/browse_url` that opens `url` as specified in event_data in the system default browser.
+**tellstick_sensor**
+Shows the values of that sensors that is connected to your Tellstick.
+
## Rest API
-Home Assistent runs a webserver accessible on port 8123.
+Home Assistent runs a webserver accessible on port 8123.
* At http://127.0.0.1:8123/ it will provide an interface allowing you to control Home Assistant.
* At http://localhost:8123/api/ it provides a password protected API.
@@ -291,17 +295,17 @@ Returns a dict with as keys the entity_ids and as value the state.
[
{
"attributes": {
- "next_rising": "07:04:15 29-10-2013",
+ "next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
- },
- "entity_id": "sun.sun",
- "last_changed": "23:24:33 28-10-2013",
+ },
+ "entity_id": "sun.sun",
+ "last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
},
{
- "attributes": {},
- "entity_id": "process.Dropbox",
- "last_changed": "23:24:33 28-10-2013",
+ "attributes": {},
+ "entity_id": "process.Dropbox",
+ "last_changed": "23:24:33 28-10-2013",
"state": "on"
}
]
@@ -313,11 +317,11 @@ Returns the current state from an entity
```json
{
"attributes": {
- "next_rising": "07:04:15 29-10-2013",
+ "next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
- },
- "entity_id": "sun.sun",
- "last_changed": "23:24:33 28-10-2013",
+ },
+ "entity_id": "sun.sun",
+ "last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
}
```
@@ -330,11 +334,11 @@ optional parameter: attributes - JSON encoded object
```json
{
"attributes": {
- "next_rising": "07:04:15 29-10-2013",
+ "next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
- },
- "entity_id": "weather.sun",
- "last_changed": "23:24:33 28-10-2013",
+ },
+ "entity_id": "weather.sun",
+ "last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
}
```
diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py
index 379c89077e2f5..5fe9dc9db947d 100644
--- a/homeassistant/components/demo.py
+++ b/homeassistant/components/demo.py
@@ -113,4 +113,16 @@ def mock_turn_off(service):
hass.states.set("chromecast.Living_Rm", "Netflix",
{'friendly_name': 'Living Room'})
+ # Setup tellstick sensors
+ hass.states.set("tellstick_sensor.Outside_temperature", "15.6",
+ {
+ 'friendly_name': 'Outside temperature',
+ 'unit_of_measurement': '°C'
+ })
+ hass.states.set("tellstick_sensor.Outside_humidity", "54",
+ {
+ 'friendly_name': 'Outside humidity',
+ 'unit_of_measurement': '°C'
+ })
+
return True
diff --git a/homeassistant/components/http/frontend.py b/homeassistant/components/http/frontend.py
index cb038c2485fb9..f091d985db9ed 100644
--- a/homeassistant/components/http/frontend.py
+++ b/homeassistant/components/http/frontend.py
@@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """
-VERSION = "560228cee9ffd6de4dfdb5816b2f9a23"
+VERSION = "feab16c797a25155a29f805b01fdd29b"
diff --git a/homeassistant/components/http/www_static/frontend.html b/homeassistant/components/http/www_static/frontend.html
index 7c071498a5468..cee525c87394f 100644
--- a/homeassistant/components/http/www_static/frontend.html
+++ b/homeassistant/components/http/www_static/frontend.html
@@ -1,364 +1,282 @@
-
-
-
-
-
-
-
+ },
+ _ensureGetter: function(i) {
+ Object.defineProperty(this, i, {
+ get: function() {
+ return this._children[i];
+ }
+ });
+ }
+};
+/** @constructor */
+var TimingGroup = function(token, type, children, timing) {
+ if (token !== constructorToken) {
+ throw new TypeError('Illegal constructor');
+ }
+ // Take a copy of the children array, as it could be modified as a side-effect
+ // of creating this object. See
+ // https://github.com/web-animations/web-animations-js/issues/65 for details.
+ var childrenCopy = (children && Array.isArray(children)) ?
+ children.slice() : [];
+ // used by TimedItem via _intrinsicDuration(), so needs to be set before
+ // initializing super.
+ this.type = type || 'par';
+ this._children = [];
+ this._cachedTimedItemList = null;
+ this._cachedIntrinsicDuration = null;
+ TimedItem.call(this, constructorToken, timing);
+ // We add children after setting the parent. This means that if an ancestor
+ // (including the parent) is specified as a child, it will be removed from our
+ // ancestors and used as a child,
+ this.append.apply(this, childrenCopy);
+};
+TimingGroup.prototype = createObject(TimedItem.prototype, {
+ _resolveFillMode: function(fillMode) {
+ return fillMode === 'auto' ? 'both' : fillMode;
+ },
+ _childrenStateModified: function() {
+ // See _updateChildStartTimes().
+ this._isInChildrenStateModified = true;
+ if (this._cachedTimedItemList) {
+ this._cachedTimedItemList._ensureGetters();
+ }
+ this._cachedIntrinsicDuration = null;
+ // We need to walk up and down the tree to re-layout. endTime and the
+ // various durations (which are all calculated lazily) are the only
+ // properties of a TimedItem which can affect the layout of its ancestors.
+ // So it should be sufficient to simply update start times and time markers
+ // on the way down.
-
+ // This calls up to our parent, then calls _updateTimeMarkers().
+ this._updateInternalState();
+ this._updateChildInheritedTimes();
-
+ // Update child start times before walking down.
+ this._updateChildStartTimes();
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+/** @constructor */
+var AnimationSequence = function(children, timing, parent) {
+ TimingGroup.call(this, constructorToken, 'seq', children, timing, parent);
+};
-
+AnimationSequence.prototype = Object.create(TimingGroup.prototype);
-
-
{{label}}
-
-
-
-
-
-
-
+/** @constructor */
+var AnimationEffect = function(token) {
+ if (token !== constructorToken) {
+ throw new TypeError('Illegal constructor');
+ }
+};
+AnimationEffect.prototype = {
+ _sample: abstractMethod,
+ clone: abstractMethod,
+ toString: abstractMethod
+};
-
+var clamp = function(x, min, max) {
+ return Math.max(Math.min(x, max), min);
+};
-
-
-
-
-
-
-
-
-
-
-
-
-
+ this.setFrames(oneOrMoreKeyframeDictionaries);
+ } finally {
+ exitModifyCurrentAnimationState(null);
+ }
+};
+KeyframeEffect.prototype = createObject(AnimationEffect.prototype, {
+ get composite() {
+ return this._composite;
+ },
+ set composite(value) {
+ enterModifyCurrentAnimationState();
+ try {
+ // Use the default value if an invalid string is specified.
+ this._composite = value === 'add' ? 'add' : 'replace';
+ } finally {
+ exitModifyCurrentAnimationState(repeatLastTick);
+ }
+ },
+ getFrames: function() {
+ return this._keyframeDictionaries.slice(0);
+ },
+ setFrames: function(oneOrMoreKeyframeDictionaries) {
+ enterModifyCurrentAnimationState();
+ try {
+ if (!Array.isArray(oneOrMoreKeyframeDictionaries)) {
+ oneOrMoreKeyframeDictionaries = [oneOrMoreKeyframeDictionaries];
+ }
+ this._keyframeDictionaries =
+ oneOrMoreKeyframeDictionaries.map(normalizeKeyframeDictionary);
+ // Set lazily
+ this._cachedPropertySpecificKeyframes = null;
+ } finally {
+ exitModifyCurrentAnimationState(repeatLastTick);
+ }
+ },
+ _sample: function(timeFraction, currentIteration, target) {
+ var frames = this._propertySpecificKeyframes();
+ for (var property in frames) {
+ compositor.setAnimatedValue(target, property,
+ this._sampleForProperty(
+ frames[property], timeFraction, currentIteration));
+ }
+ },
+ _sampleForProperty: function(frames, timeFraction, currentIteration) {
+ ASSERT_ENABLED && assert(
+ frames.length >= 2,
+ 'Interpolation requires at least two keyframes');
+ var startKeyframeIndex;
+ var length = frames.length;
+ // We extrapolate differently depending on whether or not there are multiple
+ // keyframes at offsets of 0 and 1.
+ if (timeFraction < 0.0) {
+ if (frames[1].offset === 0.0) {
+ return new AddReplaceCompositableValue(frames[0].rawValue(),
+ this._compositeForKeyframe(frames[0]));
+ } else {
+ startKeyframeIndex = 0;
+ }
+ } else if (timeFraction >= 1.0) {
+ if (frames[length - 2].offset === 1.0) {
+ return new AddReplaceCompositableValue(frames[length - 1].rawValue(),
+ this._compositeForKeyframe(frames[length - 1]));
+ } else {
+ startKeyframeIndex = length - 2;
+ }
+ } else {
+ for (var i = length - 1; i >= 0; i--) {
+ if (frames[i].offset <= timeFraction) {
+ ASSERT_ENABLED && assert(frames[i].offset !== 1.0);
+ startKeyframeIndex = i;
+ break;
+ }
+ }
+ }
+ var startKeyframe = frames[startKeyframeIndex];
+ var endKeyframe = frames[startKeyframeIndex + 1];
+ if (startKeyframe.offset === timeFraction) {
+ return new AddReplaceCompositableValue(startKeyframe.rawValue(),
+ this._compositeForKeyframe(startKeyframe));
+ }
+ if (endKeyframe.offset === timeFraction) {
+ return new AddReplaceCompositableValue(endKeyframe.rawValue(),
+ this._compositeForKeyframe(endKeyframe));
+ }
+ var intervalDistance = (timeFraction - startKeyframe.offset) /
+ (endKeyframe.offset - startKeyframe.offset);
+ if (startKeyframe.easing) {
+ intervalDistance = startKeyframe.easing.scaleTime(intervalDistance);
+ }
+ return new BlendedCompositableValue(
+ new AddReplaceCompositableValue(startKeyframe.rawValue(),
+ this._compositeForKeyframe(startKeyframe)),
+ new AddReplaceCompositableValue(endKeyframe.rawValue(),
+ this._compositeForKeyframe(endKeyframe)),
+ intervalDistance);
+ },
+ _propertySpecificKeyframes: function() {
+ if (isDefinedAndNotNull(this._cachedPropertySpecificKeyframes)) {
+ return this._cachedPropertySpecificKeyframes;
+ }
-
-
-
-
-
+ // Distribute offsets.
+ length = distributedKeyframes.length;
+ if (length > 1 && !isDefinedAndNotNull(distributedKeyframes[0].offset)) {
+ distributedKeyframes[0].offset = 0;
+ }
+ if (length > 0 &&
+ !isDefinedAndNotNull(distributedKeyframes[length - 1].offset)) {
+ distributedKeyframes[length - 1].offset = 1;
+ }
+ var lastOffsetIndex = 0;
+ var nextOffsetIndex = 0;
+ for (var i = 1; i < distributedKeyframes.length - 1; i++) {
+ var keyframe = distributedKeyframes[i];
+ if (isDefinedAndNotNull(keyframe.offset)) {
+ lastOffsetIndex = i;
+ continue;
+ }
+ if (i > nextOffsetIndex) {
+ nextOffsetIndex = i;
+ while (!isDefinedAndNotNull(
+ distributedKeyframes[nextOffsetIndex].offset)) {
+ nextOffsetIndex++;
+ }
+ }
+ var lastOffset = distributedKeyframes[lastOffsetIndex].offset;
+ var nextOffset = distributedKeyframes[nextOffsetIndex].offset;
+ var unspecifiedKeyframes = nextOffsetIndex - lastOffsetIndex - 1;
+ ASSERT_ENABLED && assert(unspecifiedKeyframes > 0);
+ var localIndex = i - lastOffsetIndex;
+ ASSERT_ENABLED && assert(localIndex > 0);
+ distributedKeyframes[i].offset = lastOffset +
+ (nextOffset - lastOffset) * localIndex / (unspecifiedKeyframes + 1);
+ }
+ // Remove invalid property values.
+ for (var i = distributedKeyframes.length - 1; i >= 0; i--) {
+ var keyframe = distributedKeyframes[i];
+ for (var property in keyframe.cssValues) {
+ if (!KeyframeInternal.isSupportedPropertyValue(
+ keyframe.cssValues[property])) {
+ delete(keyframe.cssValues[property]);
+ }
+ }
+ if (Object.keys(keyframe).length === 0) {
+ distributedKeyframes.splice(i, 1);
+ }
+ }
+ return distributedKeyframes;
+ }
+});
-
-
-
-
-
-
-
-
-
-
-
-
+/**
+ * An internal representation of a keyframe. The Keyframe type from the spec is
+ * just a dictionary and is not exposed.
+ *
+ * @constructor
+ */
+var KeyframeInternal = function(offset, composite, easing) {
+ ASSERT_ENABLED && assert(
+ typeof offset === 'number' || offset === null,
+ 'Invalid offset value');
+ ASSERT_ENABLED && assert(
+ composite === 'add' || composite === 'replace' || composite === null,
+ 'Invalid composite value');
+ this.offset = offset;
+ this.composite = composite;
+ this.easing = easing;
+ this.cssValues = {};
+};
-
-
-
-
-
-
+};
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ var v = Math.max(Math.min(parseInt(regexResult[i], 16), 255), 0);
+ out[i] = v;
+ }
+ out.push(1.0);
+ }
-
+ var regexResult = colorRE.exec(value);
+ if (regexResult) {
+ regexResult.shift();
+ var type = regexResult.shift().substr(0, 3);
+ for (var i = 0; i < 3; i++) {
+ var m = 1;
+ if (regexResult[i][regexResult[i].length - 1] === '%') {
+ regexResult[i] = regexResult[i].substr(0, regexResult[i].length - 1);
+ m = 255.0 / 100.0;
+ }
+ if (type === 'rgb') {
+ out[i] = clamp(Math.round(parseInt(regexResult[i], 10) * m), 0, 255);
+ } else {
+ out[i] = parseInt(regexResult[i], 10);
+ }
+ }
-
+ // Convert hsl values to rgb value
+ if (type === 'hsl') {
+ out = hsl2rgb.apply(null, out);
+ }
-
-
-
+function buildRotationMatcher(name, numValues, hasOptionalValue,
+ baseValue) {
+ var m = buildMatcher(name, numValues, hasOptionalValue, true, baseValue);
+ var f = function(x) {
+ var r = m[1](x);
+ return r.map(function(v) {
+ var result = 0;
+ for (var type in v) {
+ result += convertToDeg(v[type], type);
+ }
+ return result;
+ });
+ };
+ return [m[0], f, m[2]];
+}
-
+function build3DRotationMatcher() {
+ var m = buildMatcher('rotate3d', 4, false, true);
+ var f = function(x) {
+ var r = m[1](x);
+ var out = [];
+ for (var i = 0; i < 3; i++) {
+ out.push(r[i].px);
+ }
+ var angle = 0;
+ for (var unit in r[3]) {
+ angle += convertToDeg(r[3][unit], unit);
+ }
+ out.push(angle);
+ return out;
+ };
+ return [m[0], f, m[2]];
+}
-
-
-
-
-
+ function cross(v1, v2) {
+ return [v1[1] * v2[2] - v1[2] * v2[1],
+ v1[2] * v2[0] - v1[0] * v2[2],
+ v1[0] * v2[1] - v1[1] * v2[0]];
+ }
-
-
+ var perspectiveMatrix = m3d.concat(); // copy m3d
+ for (var i = 0; i < 3; i++) {
+ perspectiveMatrix[i][3] = 0;
+ }
+ if (determinant(perspectiveMatrix) === 0) {
+ return false;
+ }
-
-
+ var row = [];
+ row.push(m3d[0].slice(0, 3));
+ var scale = [];
+ scale.push(length(row[0]));
+ row[0] = normalize(row[0]);
-
+ scale.push(length(row[1]));
+ row[1] = normalize(row[1]);
+ skew[0] /= scale[1];
+ row.push(m3d[2].slice(0, 3));
+ skew.push(dot(row[0], row[2]));
+ row[2] = combine(row[2], row[0], 1.0, -skew[1]);
+ skew.push(dot(row[1], row[2]));
+ row[2] = combine(row[2], row[1], 1.0, -skew[2]);
-
-
-
+ scale.push(length(row[2]));
+ row[2] = normalize(row[2]);
+ skew[1] /= scale[2];
+ skew[2] /= scale[2];
+ var pdum3 = cross(row[1], row[2]);
+ if (dot(row[0], pdum3) < 0) {
+ for (var i = 0; i < 3; i++) {
+ scale[i] *= -1;
+ row[i][0] *= -1;
+ row[i][1] *= -1;
+ row[i][2] *= -1;
+ }
+ }
-
+ var t = row[0][0] + row[1][1] + row[2][2] + 1;
+ var s;
+ var quaternion;
-
-
-
-
+ if (t > 1e-4) {
+ s = 0.5 / Math.sqrt(t);
+ quaternion = [
+ (row[2][1] - row[1][2]) * s,
+ (row[0][2] - row[2][0]) * s,
+ (row[1][0] - row[0][1]) * s,
+ 0.25 / s
+ ];
+ } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
+ s = Math.sqrt(1 + row[0][0] - row[1][1] - row[2][2]) * 2.0;
+ quaternion = [
+ 0.25 * s,
+ (row[0][1] + row[1][0]) / s,
+ (row[0][2] + row[2][0]) / s,
+ (row[2][1] - row[1][2]) / s
+ ];
+ } else if (row[1][1] > row[2][2]) {
+ s = Math.sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0;
+ quaternion = [
+ (row[0][1] + row[1][0]) / s,
+ 0.25 * s,
+ (row[1][2] + row[2][1]) / s,
+ (row[0][2] - row[2][0]) / s
+ ];
+ } else {
+ s = Math.sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0;
+ quaternion = [
+ (row[0][2] + row[2][0]) / s,
+ (row[1][2] + row[2][1]) / s,
+ 0.25 * s,
+ (row[1][0] - row[0][1]) / s
+ ];
+ }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ if (!isDefinedAndNotNull(r)) {
+ return result;
+ }
+ }
+ return result;
+ }
+};
+var pathType = {
+ // Properties ...
+ // - path: The target path element
+ // - points: The absolute points to set on the path
+ // - cachedCumulativeLengths: The lengths at the end of each segment
+ add: function() { throw 'Addition not supported for path attribute' },
+ cumulativeLengths: function(value) {
+ if (isDefinedAndNotNull(value.cachedCumulativeLengths))
+ return value.cachedCumulativeLengths;
+ var path = value.path.cloneNode(true);
+ var cumulativeLengths = [];
+ while (path.pathSegList.numberOfItems > 0) {
+ // TODO: It would be good to skip moves here and when generating points.
+ cumulativeLengths.unshift(path.getTotalLength());
+ path.pathSegList.removeItem(path.pathSegList.numberOfItems - 1);
+ }
+ value.cachedCumulativeLengths = cumulativeLengths;
+ return value.cachedCumulativeLengths;
+ },
+ appendFractions: function(fractions, cumulativeLengths) {
+ ASSERT_ENABLED && assert(cumulativeLengths[0] === 0);
+ var totalLength = cumulativeLengths[cumulativeLengths.length - 1];
+ for (var i = 1; i < cumulativeLengths.length - 1; ++i)
+ fractions.push(cumulativeLengths[i] / totalLength);
+ },
+ interpolate: function(from, to, f) {
+ // FIXME: Handle non-linear path segments.
+ // Get the fractions at which we need to sample.
+ var sampleFractions = [0, 1];
+ pathType.appendFractions(sampleFractions, pathType.cumulativeLengths(from));
+ pathType.appendFractions(sampleFractions, pathType.cumulativeLengths(to));
+ sampleFractions.sort();
+ ASSERT_ENABLED && assert(sampleFractions[0] === 0);
+ ASSERT_ENABLED && assert(sampleFractions[sampleFractions.length - 1] === 1);
+ // FIXME: Cache the 'from' and 'to' points.
+ var fromTotalLength = from.path.getTotalLength();
+ var toTotalLength = to.path.getTotalLength();
+ var points = [];
+ for (var i = 0; i < sampleFractions.length; ++i) {
+ var fromPoint = from.path.getPointAtLength(
+ fromTotalLength * sampleFractions[i]);
+ var toPoint = to.path.getPointAtLength(
+ toTotalLength * sampleFractions[i]);
+ points.push({
+ x: interp(fromPoint.x, toPoint.x, f),
+ y: interp(fromPoint.y, toPoint.y, f)
+ });
+ }
+ return {points: points};
+ },
+ pointToString: function(point) {
+ return point.x + ',' + point.y;
+ },
+ toCssValue: function(value, svgMode) {
+ // FIXME: It would be good to use PathSegList API on the target directly,
+ // rather than generating this string, but that would require a hack to
+ // setValue().
+ ASSERT_ENABLED && assert(svgMode,
+ 'Path type should only be used with SVG \'d\' attribute');
+ if (value.path)
+ return value.path.getAttribute('d');
+ var ret = 'M' + pathType.pointToString(value.points[0]);
+ for (var i = 1; i < value.points.length; ++i)
+ ret += 'L' + pathType.pointToString(value.points[i]);
+ return ret;
+ },
+ fromCssValue: function(value) {
+ var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+ if (value)
+ path.setAttribute('d', value);
+ return {path: path};
+ }
+};
-
-
+var propertyTypes = {
+ backgroundColor: colorType,
+ backgroundPosition: positionListType,
+ borderBottomColor: colorType,
+ borderBottomLeftRadius: percentLengthType,
+ borderBottomRightRadius: percentLengthType,
+ borderBottomWidth: lengthType,
+ borderLeftColor: colorType,
+ borderLeftWidth: lengthType,
+ borderRightColor: colorType,
+ borderRightWidth: lengthType,
+ borderSpacing: lengthType,
+ borderTopColor: colorType,
+ borderTopLeftRadius: percentLengthType,
+ borderTopRightRadius: percentLengthType,
+ borderTopWidth: lengthType,
+ bottom: percentLengthAutoType,
+ boxShadow: shadowType,
+ clip: typeWithKeywords(['auto'], rectangleType),
+ color: colorType,
+ cx: lengthType,
+ cy: lengthType,
+ d: pathType,
+ dx: lengthType,
+ dy: lengthType,
+ fill: colorType,
+ floodColor: colorType,
-
+ // TODO: Handle these keywords properly.
+ fontSize: typeWithKeywords(['smaller', 'larger'], percentLengthType),
+ fontWeight: typeWithKeywords(['lighter', 'bolder'], fontWeightType),
-
+/** @constructor */
+var BlendedCompositableValue = function(startValue, endValue, fraction) {
+ this.startValue = startValue;
+ this.endValue = endValue;
+ this.fraction = fraction;
+};
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style._clearAnimatedProperty = function(property) {
+ this[property] = surrogateElement.style[property];
+ isAnimatedProperty[property] = false;
+ };
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11],
- a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11],
- a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11],
- a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11],
+
+
+
+
+
-/** @constructor */
-var CompositableValue = function() {
-};
+
-var retickThenGetComputedStyle = function() {
- repeatLastTick();
- ensureOriginalGetComputedStyle();
- return window.getComputedStyle.apply(this, arguments);
-};
+
-// This redundant flag is to support Safari which has trouble determining
-// function object equality during an animation.
-var isGetComputedStylePatched = false;
-var originalGetComputedStyle = window.getComputedStyle;
+
+
-var ensureTargetSVGInitialised = function(property, target) {
- if (!isDefinedAndNotNull(target._actuals)) {
- target._actuals = {};
- target._bases = {};
- target.actuals = {};
- target._getAttribute = target.getAttribute;
- target._setAttribute = target.setAttribute;
- target.getAttribute = function(name) {
- if (isDefinedAndNotNull(target._bases[name])) {
- return target._bases[name];
- }
- return target._getAttribute(name);
- };
- target.setAttribute = function(name, value) {
- if (isDefinedAndNotNull(target._actuals[name])) {
- target._bases[name] = value;
- } else {
- target._setAttribute(name, value);
- }
- };
- }
- if (!isDefinedAndNotNull(target._actuals[property])) {
- var baseVal = target.getAttribute(property);
- target._actuals[property] = 0;
- target._bases[property] = baseVal;
+
- Object.defineProperty(target.actuals, property, configureDescriptor({
- set: function(value) {
- if (value === null) {
- target._actuals[property] = target._bases[property];
- target._setAttribute(property, target._bases[property]);
- } else {
- target._actuals[property] = value;
- target._setAttribute(property, value);
- }
- },
- get: function() {
- return target._actuals[property];
+
+
+
+
-var ensureTargetCSSInitialised = function(target) {
- if (target.style._webAnimationsStyleInitialised) {
- return;
- }
- try {
- var animatedStyle = new AnimatedCSSStyleDeclaration(target);
- Object.defineProperty(target, 'style', configureDescriptor({
- get: function() { return animatedStyle; }
- }));
- } catch (error) {
- patchInlineStyleForAnimation(target.style);
- }
- target.style._webAnimationsStyleInitialised = true;
-};
+
+
+
- if (!isRepeat) {
- if (rafTime < lastClockTimeMillis) {
- rafTime = lastClockTimeMillis;
- }
- lastTickTime = rafTime;
- cachedClockTimeMillis = rafTime;
- }
+
+
+
-var DOCUMENT_TIMELINE = new AnimationTimeline(constructorToken);
-// attempt to override native implementation
-try {
- Object.defineProperty(document, 'timeline', {
- configurable: true,
- get: function() { return DOCUMENT_TIMELINE }
- });
-} catch (e) { }
-// maintain support for Safari
-try {
- document.timeline = DOCUMENT_TIMELINE;
-} catch (e) { }
+
+
+
-
-
-
+
+
- var size = node.getBoundingClientRect();
+
- var ink = node.querySelector('.paper-menu-button-overlay-ink');
- var offset = 40 / Math.max(size.width, size.height);
- anims.push(new Animation(ink, [{
- 'opacity': 0.9,
- 'transform': 'scale(0)',
- }, {
- 'opacity': 0.9,
- 'transform': 'scale(1)'
- }], {
- duration: this.duration * offset
- }));
+
-
-
-
-
-
-
-
-
-
-
-
-
-.paper-menu-button-overlay-ink {
- background: {{g.paperMenuButton.background}}
-}
+ findIconNames: function() {
+ var icons = this.querySelectorAll('[id]').array();
+ if (icons.length) {
+ return icons.map(function(n){ return n.id });
+ }
+ },
-.paper-menu-button-overlay-bg {
- background: {{g.paperMenuButton.background}}
-}
+ /**
+ * Applies an icon to the given element. The svg icon is added to the
+ * element's shadowRoot if one exists or directly to itself.
+ *
+ * @method applyIcon
+ * @param {Element} element The element to which the icon is
+ * applied.
+ * @param {String|Number} icon The name the icon to apply.
+ * @return {Element} The icon element
+ */
+ applyIcon: function(element, icon) {
+ var root = element;
+ // remove old
+ var old = root.querySelector('svg');
+ if (old) {
+ old.remove();
+ }
+ // install new
+ var svg = this.cloneIcon(icon);
+ if (!svg) {
+ return;
+ }
+ svg.setAttribute('height', '100%');
+ svg.setAttribute('width', '100%');
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+ svg.style.display = 'block';
+ root.insertBefore(svg, root.firstElementChild);
+ return svg;
+ },
+
+ /**
+ * Tell users of the iconset, that the set has loaded.
+ * This finds all elements matching the selector argument and calls
+ * the method argument on them.
+ * @method updateIcons
+ * @param selector {string} css selector to identify iconset users,
+ * defaults to '[icon]'
+ * @param method {string} method to call on found elements,
+ * defaults to 'updateIcon'
+ */
+ updateIcons: function(selector, method) {
+ selector = selector || '[icon]';
+ method = method || 'updateIcon';
+ var deep = window.ShadowDOMPolyfill ? '' : 'html /deep/ ';
+ var i$ = document.querySelectorAll(deep + selector);
+ for (var i=0, e; e=i$[i]; i++) {
+ if (e[method]) {
+ e[method].call(e);
+ }
+ }
+ }
+
-
+ });
-
- CoreStyle.g.paperMenuButton = CoreStyle.g.paperMenuButton || {
- 'background': '#fff'
- };
+
+
+
+
- })();
-
+
-
-
-
+
+
+
-
+
+
+
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ /****** producer stuff *******/
+ provide: function() {
+ this.register();
+ // we want to do this asap, especially so we can do so before definitions
+ // that use this core-style are registered.
+ if (this.textContent) {
+ this._completeProvide();
+ } else {
+ this.async(this._completeProvide);
+ }
+ },
-
-
+ register: function() {
+ var i = this.list[this.id];
+ if (i) {
+ if (!Array.isArray(i)) {
+ this.list[this.id] = [i];
+ }
+ this.list[this.id].push(this);
+ } else {
+ this.list[this.id] = this;
+ }
+ },
-
-
-
- // representation for dateAddRemove
- this._milliseconds = +milliseconds +
- seconds * 1e3 + // 1000
- minutes * 6e4 + // 1000 * 60
- hours * 36e5; // 1000 * 60 * 60
- // Because of dateAddRemove treats 24 hours as different from a
- // day when working around DST, we need to store them separately
- this._days = +days +
- weeks * 7;
- // It is impossible translate months into days without knowing
- // which months you are are talking about, so we have to store
- // it separately.
- this._months = +months +
- quarters * 3 +
- years * 12;
+
+
- this._data = {};
+
+
+ {{label}}
+
- this._locale = moment.localeData();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{error || validationMessage}}
+
+
+
+
+
+
- return value;
- }
+
- function daysInMonth(year, month) {
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
- }
+
+
- function weeksInYear(year, dow, doy) {
- return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week;
- }
+
- // Return a moment from input, that is local/utc/zone equivalent to model.
- function makeAs(input, model) {
- return model._isUTC ? moment(input).zone(model._offset || 0) :
- moment(input).local();
- }
+
- monthsParse : function (monthName) {
- var i, mom, regex;
+
+
+
- postformat : function (string) {
- return string;
- },
+
+
- week : function (mom) {
- return weekOfYear(mom, this._week.dow, this._week.doy).week;
- },
+
+
+
+
+
+
+
+
+
+
+
+
- /************************************
- Parsing
- ************************************/
+
+
+
- // get the regex to find the next token
- function getParseRegexForToken(token, config) {
- var a, strict = config._strict;
- switch (token) {
- case 'Q':
- return parseTokenOneDigit;
- case 'DDDD':
- return parseTokenThreeDigits;
- case 'YYYY':
- case 'GGGG':
- case 'gggg':
- return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
- case 'Y':
- case 'G':
- case 'g':
- return parseTokenSignedNumber;
- case 'YYYYYY':
- case 'YYYYY':
- case 'GGGGG':
- case 'ggggg':
- return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
- case 'S':
- if (strict) {
- return parseTokenOneDigit;
- }
- /* falls through */
- case 'SS':
- if (strict) {
- return parseTokenTwoDigits;
- }
- /* falls through */
- case 'SSS':
- if (strict) {
- return parseTokenThreeDigits;
- }
- /* falls through */
- case 'DDD':
- return parseTokenOneToThreeDigits;
- case 'MMM':
- case 'MMMM':
- case 'dd':
- case 'ddd':
- case 'dddd':
- return parseTokenWord;
- case 'a':
- case 'A':
- return config._locale._meridiemParse;
- case 'X':
- return parseTokenTimestampMs;
- case 'Z':
- case 'ZZ':
- return parseTokenTimezone;
- case 'T':
- return parseTokenT;
- case 'SSSS':
- return parseTokenDigits;
- case 'MM':
- case 'DD':
- case 'YY':
- case 'GG':
- case 'gg':
- case 'HH':
- case 'hh':
- case 'mm':
- case 'ss':
- case 'ww':
- case 'WW':
- return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
- case 'M':
- case 'D':
- case 'd':
- case 'H':
- case 'h':
- case 'm':
- case 's':
- case 'w':
- case 'W':
- case 'e':
- case 'E':
- return parseTokenOneOrTwoDigits;
- case 'Do':
- return parseTokenOrdinal;
- default :
- a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i'));
- return a;
- }
- }
+
- function timezoneMinutesFromString(string) {
- string = string || '';
- var possibleTzMatches = (string.match(parseTokenTimezone) || []),
- tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
- parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
- minutes = +(parts[1] * 60) + toInt(parts[2]);
+
- switch (token) {
- // QUARTER
- case 'Q':
- if (input != null) {
- datePartArray[MONTH] = (toInt(input) - 1) * 3;
- }
- break;
- // MONTH
- case 'M' : // fall through to MM
- case 'MM' :
- if (input != null) {
- datePartArray[MONTH] = toInt(input) - 1;
- }
- break;
- case 'MMM' : // fall through to MMMM
- case 'MMMM' :
- a = config._locale.monthsParse(input);
- // if we didn't find a month name, mark the date as invalid.
- if (a != null) {
- datePartArray[MONTH] = a;
- } else {
- config._pf.invalidMonth = input;
- }
- break;
- // DAY OF MONTH
- case 'D' : // fall through to DD
- case 'DD' :
- if (input != null) {
- datePartArray[DATE] = toInt(input);
- }
- break;
- case 'Do' :
- if (input != null) {
- datePartArray[DATE] = toInt(parseInt(input, 10));
- }
- break;
- // DAY OF YEAR
- case 'DDD' : // fall through to DDDD
- case 'DDDD' :
- if (input != null) {
- config._dayOfYear = toInt(input);
- }
+
+
- break;
- // YEAR
- case 'YY' :
- datePartArray[YEAR] = moment.parseTwoDigitYear(input);
- break;
- case 'YYYY' :
- case 'YYYYY' :
- case 'YYYYYY' :
- datePartArray[YEAR] = toInt(input);
- break;
- // AM / PM
- case 'a' : // fall through to A
- case 'A' :
- config._isPm = config._locale.isPM(input);
- break;
- // 24 HOUR
- case 'H' : // fall through to hh
- case 'HH' : // fall through to hh
- case 'h' : // fall through to hh
- case 'hh' :
- datePartArray[HOUR] = toInt(input);
- break;
- // MINUTE
- case 'm' : // fall through to mm
- case 'mm' :
- datePartArray[MINUTE] = toInt(input);
- break;
- // SECOND
- case 's' : // fall through to ss
- case 'ss' :
- datePartArray[SECOND] = toInt(input);
- break;
- // MILLISECOND
- case 'S' :
- case 'SS' :
- case 'SSS' :
- case 'SSSS' :
- datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
- break;
- // UNIX TIMESTAMP WITH MS
- case 'X':
- config._d = new Date(parseFloat(input) * 1000);
- break;
- // TIMEZONE
- case 'Z' : // fall through to ZZ
- case 'ZZ' :
- config._useUTC = true;
- config._tzm = timezoneMinutesFromString(input);
- break;
- // WEEKDAY - human
- case 'dd':
- case 'ddd':
- case 'dddd':
- a = config._locale.weekdaysParse(input);
- // if we didn't get a weekday name, mark the date as invalid
- if (a != null) {
- config._w = config._w || {};
- config._w['d'] = a;
- } else {
- config._pf.invalidWeekday = input;
- }
- break;
- // WEEK, WEEK DAY - numeric
- case 'w':
- case 'ww':
- case 'W':
- case 'WW':
- case 'd':
- case 'e':
- case 'E':
- token = token.substr(0, 1);
- /* falls through */
- case 'gggg':
- case 'GGGG':
- case 'GGGGG':
- token = token.substr(0, 2);
- if (input) {
- config._w = config._w || {};
- config._w[token] = toInt(input);
- }
- break;
- case 'gg':
- case 'GG':
- config._w = config._w || {};
- config._w[token] = moment.parseTwoDigitYear(input);
- }
- }
+
+
+
- currentDate = currentDateArray(config);
+
- //compute day of the year from weeks and weekdays
- if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- dayOfYearFromWeekInfo(config);
- }
+
- //if the day of the year is set, figure out what it is
- if (config._dayOfYear) {
- yearToUse = dfl(config._a[YEAR], currentDate[YEAR]);
+
- if (config._dayOfYear > daysInYear(yearToUse)) {
- config._pf._overflowDayOfYear = true;
- }
+
- date = makeUTCDate(yearToUse, 0, config._dayOfYear);
- config._a[MONTH] = date.getUTCMonth();
- config._a[DATE] = date.getUTCDate();
- }
+
+
{{label}}
+
+
- // Default to current date.
- // * if no year, month, day of month are given, default to today
- // * if day of month is given, default month and year
- // * if month is given, default only year
- // * if year is given, don't default anything
- for (i = 0; i < 3 && config._a[i] == null; ++i) {
- config._a[i] = input[i] = currentDate[i];
- }
+
+
+
+
+
+
+
+
+
+
- tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- if (data) {
- moment.duration._locale = moment._locale = data;
- }
- }
+
- moment.defineLocale = function (name, values) {
- if (values !== null) {
- values.abbr = name;
- if (!locales[name]) {
- locales[name] = new Locale();
- }
- locales[name].set(values);
+
- // backwards compat for now: also set the locale
- moment.locale(name);
+
- return locales[name];
- } else {
- // useful for testing
- delete locales[name];
- return null;
- }
- };
+
+
+
+
+
+
+
+
- return m;
- };
-
- moment.parseZone = function () {
- return moment.apply(null, arguments).parseZone();
- };
-
- moment.parseTwoDigitYear = function (input) {
- return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
- };
+
+
+
+
- /************************************
- Moment Prototype
- ************************************/
+
+
+
+
+
+
+
+
+
+
+
- max: deprecate(
- 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
- function (other) {
- other = moment.apply(null, arguments);
- return other > this ? this : other;
- }
- ),
+
+
+
+
+
+
- if (key === undefined) {
- return this._locale._abbr;
- } else {
- newLocaleData = moment.localeData(key);
- if (newLocaleData != null) {
- this._locale = newLocaleData;
- }
- return this;
- }
- },
+
+
+
+
+
+
- lang : deprecate(
- 'moment().lang() is deprecated. Use moment().localeData() instead.',
- function (key) {
- if (key === undefined) {
- return this.localeData();
- } else {
- return this.locale(key);
- }
- }
- ),
+
+
+
+
+
+
- _bubble : function () {
- var milliseconds = this._milliseconds,
- days = this._days,
- months = this._months,
- data = this._data,
- seconds, minutes, hours, years = 0;
+
+
+
- // The following code bubbles up values, see the tests for
- // examples of what that means.
- data.milliseconds = milliseconds % 1000;
+
+
+
- seconds = absRound(milliseconds / 1000);
- data.seconds = seconds % 60;
+.paper-menu-button-overlay-ink {
+ background: {{g.paperMenuButton.background}}
+}
- minutes = absRound(seconds / 60);
- data.minutes = minutes % 60;
+.paper-menu-button-overlay-bg {
+ background: {{g.paperMenuButton.background}}
+}
- hours = absRound(minutes / 60);
- data.hours = hours % 24;
+
- days += absRound(hours / 24);
+
- // 12 months -> 1 year
- years += absRound(months / 12);
- months %= 12;
+
+
- data.days = days;
- data.months = months;
- data.years = years;
- },
+
+
- return this;
- },
+
- get : function (units) {
- units = normalizeUnits(units);
- return this[units.toLowerCase() + 's']();
- },
+
- as : function (units) {
- var days, months;
- units = normalizeUnits(units);
+
- if (units === 'month' || units === 'year') {
- days = this._days + this._milliseconds / 864e5;
- months = this._months + daysToYears(days) * 12;
- return units === 'month' ? months : months / 12;
- } else {
- // handle milliseconds separately because of floating point math errors (issue #1867)
- days = this._days + yearsToDays(this._months / 12);
- switch (units) {
- case 'week': return days / 7 + this._milliseconds / 6048e5;
- case 'day': return days + this._milliseconds / 864e5;
- case 'hour': return days * 24 + this._milliseconds / 36e5;
- case 'minute': return days * 24 * 60 + this._milliseconds / 6e4;
- case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000;
- // Math.floor prevents floating point math errors here
- case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds;
- default: throw new Error('Unknown unit ' + units);
- }
- }
- },
+
+
- lang : moment.fn.lang,
- locale : moment.fn.locale,
+