Merge "Adds camlistore.Spinner, a progress meter based on the safe icon."

This commit is contained in:
Aaron Boodman 2013-12-01 18:09:50 +00:00 committed by Gerrit Code Review
commit bffbb0b349
8 changed files with 466 additions and 2 deletions

View File

@ -0,0 +1,128 @@
/*
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
goog.provide('camlistore.AnimationLoop');
goog.require('goog.events.EventTarget');
/**
* Provides an easier-to-use interface around
* window.requestAnimationFrame(), and abstracts away browser differences.
* @param {Window} win
*/
camlistore.AnimationLoop = function(win) {
/**
* @type {Window}
* @private
*/
this.win_ = win;
/**
* @type {Function}
* @private
*/
this.requestAnimationFrame_ = win.requestAnimationFrame ||
win.mozRequestAnimationFrame || win.webkitRequestAnimationFrame ||
win.msRequestAnimationFrame;
/**
* @type {Function}
* @private
*/
this.handleFrame_ = this.handleFrame_.bind(this);
/**
* @type {number}
* @private
*/
this.lastTimestamp_ = 0;
if (this.requestAnimationFrame_) {
this.requestAnimationFrame_ = this.requestAnimationFrame_.bind(win);
} else {
this.requestAnimationFrame_ = this.simulateAnimationFrame_.bind(this);
}
};
goog.inherits(camlistore.AnimationLoop, goog.events.EventTarget);
/**
* @type {string}
*/
camlistore.AnimationLoop.FRAME_EVENT_TYPE = 'frame';
/**
* @returns {boolean}
*/
camlistore.AnimationLoop.prototype.isRunning = function() {
return Boolean(this.lastTimestamp_);
};
camlistore.AnimationLoop.prototype.start = function() {
if (this.isRunning()) {
return;
}
this.lastTimestamp_ = -1;
this.schedule_();
};
camlistore.AnimationLoop.prototype.stop = function() {
this.lastTimestamp_ = 0;
};
/**
* @private
*/
camlistore.AnimationLoop.prototype.schedule_ = function() {
this.requestAnimationFrame_(this.handleFrame_);
};
/**
* @param {number=} opt_timestamp A timestamp in milliseconds that is used to
* measure progress through the animation.
* @private
*/
camlistore.AnimationLoop.prototype.handleFrame_ = function(opt_timestamp) {
if (this.lastTimestamp_ == 0) {
return;
}
var timestamp = opt_timestamp || new Date().getTime();
if (this.lastTimestamp_ == -1) {
this.lastTimestamp_ = timestamp;
} else {
this.dispatchEvent({
type: this.constructor.FRAME_EVENT_TYPE,
delay: timestamp - this.lastTimestamp_
});
this.lastTimestamp_ = timestamp;
}
this.schedule_();
};
/**
* Simulates requestAnimationFrame as best as possible for browsers that don't
* have it.
* @param {Function} fn
* @private
*/
camlistore.AnimationLoop.prototype.simulateAnimationFrame_ = function(fn) {
this.win_.setTimeout(function() {
fn(new Date().getTime());
}, 0);
};

View File

@ -0,0 +1,23 @@
<!--
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="500px" height="500px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<g id="Captions">
</g>
<g id="Your_Icon">
<path d="M89.473,0H10.527C4.713,0,0,4.712,0,10.526v78.948C0,95.287,4.713,100,10.527,100h78.946 C95.286,100,100,95.287,100,89.474V10.526C100,4.712,95.286,0,89.473,0z M61.251,58.234 c-3.455,10.827-13.464,17.742-24.258,17.739c-2.567,0-5.174-0.391-7.741-1.206l0.799-2.51l-0.801,2.51 C18.423,71.31,11.509,61.3,11.511,50.507c0-2.566,0.391-5.173,1.208-7.74c3.456-10.829,13.467-17.742,24.26-17.742 c2.563,0,5.175,0.391,7.741,1.209c10.829,3.458,17.743,13.465,17.74,24.259C62.46,53.061,62.069,55.668,61.251,58.234z M89.765,82.237c0,2.665-2.16,4.824-4.824,4.824c-2.665,0-4.825-2.159-4.825-4.824V17.763c0-2.666,2.16-4.825,4.825-4.825 c2.664,0,4.824,2.159,4.824,4.825V82.237z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,28 @@
<!--
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="500px" height="500px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<g id="Captions">
</g>
<g id="Your_Icon">
<g>
<path d="M36.818,59.693l-3.399,10.659c1.197,0.217,2.391,0.358,3.575,0.358c7.705,0,14.884-4.472,18.241-11.63l-10.654-3.396 C42.833,58.243,39.917,59.748,36.818,59.693z"/>
<path d="M37.154,41.308l3.4-10.665c-1.198-0.214-2.391-0.355-3.576-0.355c-7.705,0.004-14.886,4.471-18.243,11.632l10.659,3.399 C31.142,42.755,34.057,41.251,37.154,41.308z"/>
<path d="M45.562,32.255l-3.394,10.652c2.56,1.748,4.068,4.66,4.012,7.762l10.664,3.4c0.214-1.197,0.354-2.393,0.354-3.576 C57.195,42.792,52.724,35.609,45.562,32.255z"/>
<path d="M27.796,50.333l-10.667-3.399c-0.216,1.196-0.355,2.392-0.355,3.574c0.002,7.703,4.474,14.886,11.635,18.24l3.396-10.653 C29.246,56.347,27.74,53.434,27.796,50.333z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="500px" height="500px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<g id="Captions">
</g>
<g id="Your_Icon">
@ -7,7 +7,7 @@
<path d="M37.154,41.308l3.4-10.665c-1.198-0.214-2.391-0.355-3.576-0.355c-7.705,0.004-14.886,4.471-18.243,11.632l10.659,3.399 C31.142,42.755,34.057,41.251,37.154,41.308z"/>
<path d="M45.562,32.255l-3.394,10.652c2.56,1.748,4.068,4.66,4.012,7.762l10.664,3.4c0.214-1.197,0.354-2.393,0.354-3.576 C57.195,42.792,52.724,35.609,45.562,32.255z"/>
<path d="M27.796,50.333l-10.667-3.399c-0.216,1.196-0.355,2.392-0.355,3.574c0.002,7.703,4.474,14.886,11.635,18.24l3.396-10.653 C29.246,56.347,27.74,53.434,27.796,50.333z"/>
<path d="M89.473,0H10.527C4.713,0,0,4.712,0,10.526v78.948C0,95.287,4.713,100,10.527,100h78.946 C95.286,100,100,95.287,100,89.474V10.526C100,4.712,95.286,0,89.473,0z M61.251,58.234 c-3.455,10.827-13.464,17.742-24.258,17.739c-2.567,0-5.174-0.391-7.741-1.206l0.799-2.51l-0.801,2.51 C18.423,71.31,11.509,61.3,11.511,50.507c0-2.566,0.391-5.173,1.208-7.74c3.456-10.829,13.467-17.742,24.26-17.742 c2.563,0,5.175,0.391,7.741,1.209c10.829,3.458,17.743,13.465,17.74,24.259C62.46,53.061,62.069,55.668,61.251,58.234z M89.765,82.237c0,2.665-2.16,4.824-4.824,4.824c-2.665,0-4.825-2.159-4.825-4.824V17.763c0-2.666,2.16-4.825,4.825-4.825 c2.664,0,4.824,2.159,4.824,4.825V82.237z"/>
</g>
<path d="M89.473,0H10.527C4.713,0,0,4.712,0,10.526v78.948C0,95.287,4.713,100,10.527,100h78.946 C95.286,100,100,95.287,100,89.474V10.526C100,4.712,95.286,0,89.473,0z M61.251,58.234 c-3.455,10.827-13.464,17.742-24.258,17.739c-2.567,0-5.174-0.391-7.741-1.206l0.799-2.51l-0.801,2.51 C18.423,71.31,11.509,61.3,11.511,50.507c0-2.566,0.391-5.173,1.208-7.74c3.456-10.829,13.467-17.742,24.26-17.742 c2.563,0,5.175,0.391,7.741,1.209c10.829,3.458,17.743,13.465,17.74,24.259C62.46,53.061,62.069,55.668,61.251,58.234z M89.765,82.237c0,2.665-2.16,4.824-4.824,4.824c-2.665,0-4.825-2.159-4.825-4.824V17.763c0-2.666,2.16-4.825,4.825-4.825 c2.664,0,4.824,2.159,4.824,4.825V82.237z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,29 @@
/*
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.cam-spinner {
position: relative;
background-size: 100%;
}
.cam-spinner>div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: 100%;
}

View File

@ -0,0 +1,141 @@
/*
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
goog.provide('camlistore.Spinner');
goog.require('camlistore.AnimationLoop');
goog.require('camlistore.style');
goog.require('goog.dom');
goog.require('goog.events.EventHandler');
goog.require('goog.style');
goog.require('goog.math.Coordinate');
goog.require('goog.math.Size');
goog.require('goog.ui.Control');
/**
* An indeterminite progress meter using the safe icon.
* @param {goog.dom.DomHelper} domHelper
*/
camlistore.Spinner = function(domHelper) {
goog.base(this, null, this.dom_);
/**
* @type {goog.dom.DomHelper}
* @private
*/
this.dom_ = domHelper;
/**
* @type {goog.events.EventHandler}
* @private
*/
this.eh_ = new goog.events.EventHandler(this);
/**
* @type {camlistore.AnimationLoop}
* @private
*/
this.animationLoop_ = new camlistore.AnimationLoop(this.dom_.getWindow());
/**
* @type {number}
* @private
*/
this.currentRotation_ = 0;
};
goog.inherits(camlistore.Spinner, goog.ui.Control);
/**
* @type {string}
*/
camlistore.Spinner.prototype.backgroundImage = "safe-no-wheel.svg";
/**
* @type {string}
*/
camlistore.Spinner.prototype.foregroundImage = "safe-wheel.svg";
/**
* @type {number}
*/
camlistore.Spinner.prototype.degreesPerSecond = 500;
/**
* The origin the safe wheel rotates around, expressed as a fraction of the
* image's width and height.
*
* @type {goog.math.Coordinate}
* @private
*/
camlistore.Spinner.prototype.wheelRotationOrigin_ =
new goog.math.Coordinate(0.37, 0.505);
/**
* @override
*/
camlistore.Spinner.prototype.createDom = function() {
this.background_ = this.dom_.createDom('div', 'cam-spinner',
this.dom_.createDom('div'));
this.foreground_ = this.background_.firstChild;
camlistore.style.setURLStyle(this.background_, 'background-image',
this.backgroundImage);
camlistore.style.setURLStyle(this.foreground_, 'background-image',
this.foregroundImage);
// TODO(aa): This will need to be configurable. Not sure how makes sense yet.
var size = new goog.math.Size(75, 75);
goog.style.setSize(this.background_, size);
// We should be able to set the origin as a percentage directly, but the
// browsers end up rounding differently, and we get less off-center spinning
// on the whole if we set this using pixels.
var origin = new goog.math.Coordinate(size.width, size.height);
camlistore.style.setTransformOrigin(
this.foreground_,
origin.scale(this.wheelRotationOrigin_.x,
this.wheelRotationOrigin_.y));
this.eh_.listen(this.animationLoop_,
camlistore.AnimationLoop.FRAME_EVENT_TYPE,
this.updateRotation_);
this.decorateInternal(this.background_);
};
camlistore.Spinner.prototype.isRunning = function() {
return this.animationLoop_.isRunning();
};
camlistore.Spinner.prototype.start = function() {
this.animationLoop_.start();
};
camlistore.Spinner.prototype.stop = function() {
this.animationLoop_.stop();
};
/**
* @private
*/
camlistore.Spinner.prototype.updateRotation_ = function(e) {
rotation = e.delay / 1000 * this.degreesPerSecond;
this.currentRotation_ += rotation;
this.currentRotation_ %= 360;
camlistore.style.setRotation(this.foreground_, this.currentRotation_);
};

View File

@ -0,0 +1,45 @@
<!--
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="spinner.css">
<script src="closure/goog/base.js"></script>
<script src="deps.js"></script>
<script>
goog.require('camlistore.Spinner');
</script>
</head>
<body>
<script>
var spinner = new camlistore.Spinner(goog.dom.getDomHelper());
spinner.render(document.body);
document.onclick = function() {
spinner.isRunning() ? spinner.stop() : spinner.start();
};
// These two are to easily see how off-center the rotation is at worst.
spinner.getElement().onmouseover = function() {
camlistore.style.setRotation(spinner.getElement().firstChild, 180);
};
spinner.getElement().onmouseout = function() {
camlistore.style.setRotation(spinner.getElement().firstChild, 0);
};
</script>
</body>
</html>

View File

@ -0,0 +1,70 @@
/*
Copyright 2013 The Camlistore Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview Some extra style utilties above what's included in goog.style.
*/
goog.provide('camlistore.style');
goog.require('goog.math.Coordinate');
goog.require('goog.string');
goog.require('goog.style');
/**
* Returns |url| wrapped in url() so that it can be used as a CSS property
* value.
* @param {string} url
* @returns {string}
*/
camlistore.style.getURLValue = function(url) {
return goog.string.subs('url(%s)', url);
};
/**
* Sets a style property to a URL value.
* @param {Element} elm
* @param {string} dashedCSSProperty The CSS property to set, formatted with
* dashes, in the CSS style, not camelCase.
* @param {string} url
*/
camlistore.style.setURLStyle = function(elm, dashedCSSProperty, url) {
goog.style.setStyle(elm, dashedCSSProperty,
camlistore.style.getURLValue(url));
};
/**
* @param {Element} elm
* @param {goog.math.Coordinate} origin
* @param {string=} opt_unit The CSS units the origin is in. If unspecified,
* defaults to pixels.
*/
camlistore.style.setTransformOrigin = function(elm, origin, opt_unit) {
var unit = opt_unit || 'px';
goog.style.setStyle(elm, 'transform-origin',
goog.string.subs('%s%s %s%s', origin.x, unit, origin.y,
unit));
};
/**
* Note that this currently clears any previous CSS transform. Currently we only
* needs to support rotate().
* @param {Element} elm
* @param {number} degrees
*/
camlistore.style.setRotation = function(elm, degrees) {
goog.style.setStyle(elm, 'transform',
goog.string.subs('rotate(%sdeg)', degrees));
};