mirror of https://github.com/perkeep/perkeep.git
Merge "Adds camlistore.Spinner, a progress meter based on the safe icon."
This commit is contained in:
commit
bffbb0b349
|
@ -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);
|
||||
};
|
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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%;
|
||||
}
|
|
@ -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_);
|
||||
};
|
|
@ -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>
|
|
@ -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));
|
||||
};
|
Loading…
Reference in New Issue