<!DOCTYPE html>
<html>
<head>
<title>Angular Block Spinner</title>
<link rel="stylesheet" type="text/css" href="angular-loading.css" />
<link rel="stylesheet" type="text/css" href="style.css" />
<script data-require="angular.js@1.2.13" data-semver="1.2.13" src="http://code.angularjs.org/1.2.13/angular.js"></script>
<script data-require="spin.js@1.2.7" data-semver="1.2.7" src="//cdnjs.cloudflare.com/ajax/libs/spin.js/1.2.7/spin.min.js"></script>
<script type="text/javascript" src="angular-loading.js"></script>
<script>
angular.module('demoApp', [
'darthwade.dwLoading'
])
.controller('MainCtrl', function ($scope, $loading) {
$scope.startLoading = function (name) {
$loading.start(name);
};
$scope.finishLoading = function (name) {
$loading.finish(name);
};
})
</script>
</head>
<body ng-app="demoApp">
<div ng-controller="MainCtrl">
<div class="sample sample-1">
<h2>Basic</h2>
<input type="button" value="Start Loading" ng-click="startLoading('sample-1')" />
<input type="button" value="Finish Loading" ng-click="finishLoading('sample-1')" />
<div class="box small" dw-loading="sample-1">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
</div>
</div>
<div class="sample sample-2">
<h2>Autoload</h2>
<input type="button" value="Start Loading" ng-click="startLoading('sample-2')" />
<input type="button" value="Finish Loading" ng-click="finishLoading('sample-2')" />
<div class="box small" dw-loading="sample-2" dw-loading-options="{active: true}">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
</div>
</div>
<div class="sample sample-3">
<h2>No text, no overlay</h2>
<input type="button" value="Start Loading" ng-click="startLoading('sample-3')" />
<input type="button" value="Finish Loading" ng-click="finishLoading('sample-3')" />
<div class="box small" dw-loading="sample-3" dw-loading-options="{active: true, text: false, overlay: false}">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
</div>
</div>
<div class="sample sample-4">
<h2>Custom styling, custom spinner, custom text</h2>
<input type="button" value="Start Loading" ng-click="startLoading('sample-4')" />
<input type="button" value="Finish Loading" ng-click="finishLoading('sample-4')" />
<div class="box small" dw-loading="sample-4" dw-loading-options="{active: true, text: 'Custom text', className: 'custom-loading', spinnerOptions: {lines: 12, length: 20, width: 6, radius: 20, color: '#f0f', direction: -1, speed: 3}}">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
</div>
</div>
</div>
</body>
</html>
/* for testing purposes */
body {
margin:80px 10px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 18px;
color: #333333;
background-color: #ffffff;
}
.box {
padding:20px;
border:1px solid #000;
position:relative;
}
.small {
width:200px;
height:200px;
}
.medium {
width:500px;
height:350px;
}
.large {
width:600px;
height:500px;
}
.sample {
float: left;
width: 270px;
}
/* custom loader styles */
.custom-loading.dw-loading {
text-align: center;
}
.custom-loading.dw-loading.dw-loading-overlay {
background-color: rgba(0, 255, 255, .7);
z-index: 9999;
}
.custom-loading.dw-loading > .dw-loading-body > .dw-loading-text {
color: green;
font-size: 13px;
top: 45px;
}
(function (window, angular, undefined) {
'use strict';
angular.module('darthwade.dwLoading', [])
.value('dwLoadingOptions', {
active: false,
text: 'Loading...',
className: '',
overlay: true,
spinner: true,
spinnerOptions: {
lines: 12, // The number of lines to draw
length: 7, // The length of each line
width: 4, // The line thickness
radius: 10, // The radius of the inner circle
rotate: 0, // Rotation offset
corners: 1, // Roundness (0..1)
color: '#000', // #rgb or #rrggbb
direction: 1, // 1: clockwise, -1: counterclockwise
speed: 2, // Rounds per second
trail: 100, // Afterglow percentage
opacity: 1/4, // Opacity of the lines
fps: 20, // Frames per second when using setTimeout()
zIndex: 2e9, // Use a high z-index by default
className: 'dw-spinner', // CSS class to assign to the element
top: 'auto', // center vertically
left: 'auto', // center horizontally
position: 'relative' // element position
}
})
.service('dwLoading', ['$rootScope', 'dwLoadingOptions', function ($rootScope, dwLoadingOptions) {
var self = this;
/**
* Sets default options (@see `dwLoadingOptions`)
* @param {object} options
*/
self.setDefaultOptions = function (options) {
extend(true, dwLoadingOptions, options);
};
/**
* Activates spinner by key
* @param {string} key
*/
self.start = function (key) {
$rootScope.$broadcast('$dwLoadingStart', key);
};
/**
* Deactivates spinner by key
* @param {string} key
*/
self.finish = function (key) {
$rootScope.$broadcast('$dwLoadingFinish', key);
};
}])
// Shortcut
.factory('$loading', ['dwLoading', function(dwLoading) {
return dwLoading;
}])
.directive('dwLoading', ['$window', '$compile', 'dwLoadingOptions', function ($window, $compile, dwLoadingOptions) {
return {
scope: true,
link: function (scope, element, attrs) {
scope.spinner = null;
scope.key = attrs.dwLoading || false;
/**
* Starts spinner
*/
scope.start = function () {
if (scope.container) {
scope.container.addClass('dw-loading-active');
}
if (scope.spinner) {
scope.spinner.spin(scope.spinnerContainer[0]);
}
};
/**
* Stops spinner
*/
scope.finish = function () {
if (scope.container) {
scope.container.removeClass('dw-loading-active');
}
if (scope.spinner) {
scope.spinner.stop();
}
};
scope.$watch(attrs.dwLoadingOptions, function (options) {
scope.finish();
scope.options = extend(true, {}, dwLoadingOptions, options);
// Build template
var body = angular.element('<div></div>')
.addClass('dw-loading-body');
scope.container = angular.element('<div></div>')
.addClass('dw-loading')
.append(body);
if (scope.options.overlay) {
scope.container.addClass('dw-loading-overlay');
}
if (scope.options.className) {
scope.container.addClass(scope.options.className);
}
if (scope.options.spinner) {
scope.spinnerContainer = angular.element('<div></div>')
.addClass('dw-loading-spinner');
body.append(scope.spinnerContainer);
scope.spinner = new $window.Spinner(scope.options.spinnerOptions);
}
if (scope.options.text) {
var text = angular.element('<div></div>')
.addClass('dw-loading-text')
.text(scope.options.text);
body.append(text);
}
element.append(scope.container);
// $compile(container)(scope);
if (scope.options.active || !scope.key) {
scope.start();
}
}, true);
scope.$on('$dwLoadingStart', function (event, key) {
if (key === scope.key) {
scope.start();
}
});
scope.$on('$dwLoadingFinish', function (event, key) {
if (key === scope.key) {
scope.finish();
}
});
scope.$on('$destroy', function () {
scope.finish();
scope.spinner = null;
});
}
};
}]);
/**
* Extends the destination object `dst` by copying all of the properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects.
*
* @param {Boolean} deep If true, the merge becomes recursive (optional)
* @param {Object} dst Destination object.
* @param {Object} src Source object(s).
* @returns {Object} Reference to `dst`.
*/
function extend(dst) {
var deep = false,
i = 1;
if (typeof dst === 'boolean') {
deep = dst;
dst = arguments[1] || {};
i++;
}
angular.forEach([].slice.call(arguments, i), function (obj) {
var array, clone, copy, key, src;
for (key in obj) {
src = dst[key];
copy = obj[key];
if (dst === copy) {
continue;
}
if (deep && copy && (angular.isObject(copy) ||
(array = angular.isArray(copy)))) {
if (array) {
clone = (src && angular.isArray(src)) ? src : [];
} else {
clone = (src && angular.isObject(src)) ? src : {};
}
dst[key] = extend(deep, clone, copy);
}
else if (copy !== undefined) {
dst[key] = copy;
}
}
});
return dst;
}
})(window, window.angular);
.dw-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
text-align: center;
display: none;
}
.dw-loading:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.dw-loading.dw-loading-active {
display: block;
}
.dw-loading.dw-loading-overlay {
background-color: rgba(255, 255, 255, .7);
z-index: 9999;
}
.dw-loading > .dw-loading-body {
display: inline-block;
vertical-align: middle;
position: relative;
}
.dw-loading > .dw-loading-body > .dw-loading-spinner {
position: relative;
}
.dw-loading > .dw-loading-body > .dw-loading-text {
position: relative;
top: 25px;
font-weight: bold;
font-size: 11px;
text-shadow: 0 0 2px rgb(255, 255, 255);
}