From c08fd6d5697c0c6668dd689b1a0df6d04e216b59 Mon Sep 17 00:00:00 2001 From: Alexander Machehin Date: Wed, 2 Apr 2014 16:32:35 +0600 Subject: [PATCH] [#369] add angular spinner; some fixes --- Gemfile | 1 + Gemfile.lock | 2 + .../controllers/activity_controller.js | 23 ++ app/assets/javascripts/new_application.js | 9 +- .../stylesheets/new_application.css.scss | 3 +- app/views/home/activity.html.haml | 16 +- app/views/layouts/bootstrap.html.haml | 2 +- app/views/layouts/menu/_new_bottom.html.haml | 2 +- app/views/layouts/menu/_new_top.html.haml | 6 +- vendor/assets/javascripts/angular-busy.js | 116 +++++++ vendor/assets/javascripts/promise-tracker.js | 242 ++++++++++++++ vendor/assets/stylesheets/angular-busy.css | 295 ++++++++++++++++++ 12 files changed, 706 insertions(+), 11 deletions(-) create mode 100644 app/assets/javascripts/angular-new/controllers/activity_controller.js create mode 100644 vendor/assets/javascripts/angular-busy.js create mode 100644 vendor/assets/javascripts/promise-tracker.js create mode 100644 vendor/assets/stylesheets/angular-busy.css diff --git a/Gemfile b/Gemfile index 36b9a31e7..c70ca8cda 100644 --- a/Gemfile +++ b/Gemfile @@ -74,6 +74,7 @@ gem 'momentjs-rails' gem 'angular-i18n', '0.1.2' gem 'js-routes' gem 'soundmanager-rails' +gem 'angular-ui-bootstrap-rails' gem 'time_diff' diff --git a/Gemfile.lock b/Gemfile.lock index 8615e9108..8f64d7205 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -64,6 +64,7 @@ GEM ancestry (2.0.0) activerecord (>= 3.0.0) angular-i18n (0.1.2) + angular-ui-bootstrap-rails (0.10.0) angularjs-rails (1.2.14) arel (4.0.2) atomic (1.1.16) @@ -465,6 +466,7 @@ DEPENDENCIES airbrake (~> 3.1.2) ancestry (~> 2.0.0) angular-i18n (= 0.1.2) + angular-ui-bootstrap-rails angularjs-rails attr_encrypted (~> 1.3.2) better_errors diff --git a/app/assets/javascripts/angular-new/controllers/activity_controller.js b/app/assets/javascripts/angular-new/controllers/activity_controller.js new file mode 100644 index 000000000..7e355bc92 --- /dev/null +++ b/app/assets/javascripts/angular-new/controllers/activity_controller.js @@ -0,0 +1,23 @@ +var RosaABF = angular.module('RosaABF', ['ui.bootstrap', 'cgBusy']); + +RosaABF.controller('ActivityCtrl', ['$scope', '$http', '$timeout', 'promiseTracker', '$q', + function($scope, $http, $timeout, promiseTracker, $q) { + $scope.tabs = [ + { title:"All", content:[] , isLoaded:false , active:true, filter:'all'}, + { title:"Issues", content:[] , isLoaded:false, filter:'issues' } + ]; + + $scope.getContent=function(tabIndex){ + + /* see if we have data already */ + if($scope.tabs[tabIndex].isLoaded){ + return + } + /* or make request for data */ + var path = Routes.root_path({ filter: $scope.tabs[tabIndex].filter, format: 'json' }); + $http.get(path, { tracker: 'activity' }).then(function(res){ + $scope.tabs[tabIndex].content=res.data; + $scope.tabs[tabIndex].isLoaded=true; + }); + } +}]); diff --git a/app/assets/javascripts/new_application.js b/app/assets/javascripts/new_application.js index ef7d6a302..a68f79f82 100644 --- a/app/assets/javascripts/new_application.js +++ b/app/assets/javascripts/new_application.js @@ -1,3 +1,10 @@ //= require jquery +//= require js-routes // Loads all Bootstrap javascripts -//= require bootstrap \ No newline at end of file +//= require bootstrap +//= require unstable/angular +// require angular +//= require angular-ui-bootstrap-tpls +//= require_tree ./angular-new +//= require angular-busy +//= require promise-tracker \ No newline at end of file diff --git a/app/assets/stylesheets/new_application.css.scss b/app/assets/stylesheets/new_application.css.scss index fd92f43ff..8c25df7f6 100644 --- a/app/assets/stylesheets/new_application.css.scss +++ b/app/assets/stylesheets/new_application.css.scss @@ -6,4 +6,5 @@ $navbar-height: 51px; $navbar-default-link-hover-color: #CEE7FF; @import "bootstrap"; -@import "custom_bootstrap" +@import "custom_bootstrap"; +@import "angular-busy"; diff --git a/app/views/home/activity.html.haml b/app/views/home/activity.html.haml index 9ed9b737d..dffad3518 100644 --- a/app/views/home/activity.html.haml +++ b/app/views/home/activity.html.haml @@ -2,8 +2,16 @@ %h3.fix = t("layout.activity_feed.header") = link_to image_tag("rss.ico", width: '15px', height: '15px', class: 'atom_icon'), atom_activity_feeds_path(format: 'atom', token: current_user.authentication_token) - =render 'feed_tabs' -=render 'list' +/ =#render 'feed_tabs' +/ =render 'list' -- content_for :sidebar, render('sidebar') -- render 'top_menu' +/ - content_for :sidebar, render('sidebar') +/ - render 'top_menu' + +%div{ 'ng-controller' => 'ActivityCtrl', "cg-busy" => "'activity'" } + %tabset + %tab{ 'ng-repeat' => "tab in tabs", 'heading' => "{{tab.title}}", 'active' => "tab.active", 'select' => 'getContent($index)' } + %div{ 'ng-hide' => "!tab.isLoaded" } + %h5 Content 1 + %div{ 'ng-repeat' => "item in tab.content" } + {{item}} diff --git a/app/views/layouts/bootstrap.html.haml b/app/views/layouts/bootstrap.html.haml index 9ff2728db..9e556b8ce 100644 --- a/app/views/layouts/bootstrap.html.haml +++ b/app/views/layouts/bootstrap.html.haml @@ -7,7 +7,7 @@ - if user_signed_in? = auto_discovery_link_tag :atom, atom_activity_feeds_path(format: 'atom', token: current_user.authentication_token), title: t("layout.atom_link_tag_title", nickname: current_user.uname, app_name: APP_CONFIG['project_name']) - %body + %body{ 'ng-app' => 'RosaABF' } = render 'layouts/menu/new_top' %article.container-fluid .row= yield diff --git a/app/views/layouts/menu/_new_bottom.html.haml b/app/views/layouts/menu/_new_bottom.html.haml index 4981f3249..2b2761997 100644 --- a/app/views/layouts/menu/_new_bottom.html.haml +++ b/app/views/layouts/menu/_new_bottom.html.haml @@ -2,7 +2,7 @@ %ul %li= t('bottom_menu.copyright', year: Date.today.year) %li · - %li= image_tag 'flag.png', alt: 'flag' + %li= image_tag 'flag.png', alt: 'flag', class: 'img-responsive' %li · %li= link_to t('bottom_menu.about'), t('bottom_menu.about_url') %li · diff --git a/app/views/layouts/menu/_new_top.html.haml b/app/views/layouts/menu/_new_top.html.haml index 737d729af..fa276c8ff 100644 --- a/app/views/layouts/menu/_new_top.html.haml +++ b/app/views/layouts/menu/_new_top.html.haml @@ -2,14 +2,14 @@ .container-fluid / Brand and toggle get grouped for better mobile display .navbar-header - %button.navbar-toggle{ "data-target" => "#bs-example-navbar-collapse-1", "data-toggle" => "collapse", type: "button" } + %button.navbar-toggle{ "data-target" => "#top-menu-navbar-collapse-1", "data-toggle" => "collapse", type: "button" } %span.sr-only Toggle navigation %span.icon-bar %span.icon-bar %span.icon-bar = link_to image_tag('logo-mini.png', alt: 'ABF'), root_path, class: 'navbar-brand' / Collect the nav links, forms, and other content for toggling - #bs-example-navbar-collapse-1.collapse.navbar-collapse + #top-menu-navbar-collapse-1.collapse.navbar-collapse %ul.nav.navbar-nav - (collection = t 'top_menu').each do |base, title| - if can? :index, base.to_s.classify.constantize @@ -21,7 +21,7 @@ = text_field_tag 'query', @query, placeholder: t('layout.search.header'), class: 'form-control' %ul.nav.navbar-nav.navbar-right %li - .avatar= image_tag avatar_url(current_user), alt: 'avatar', height: "32" + .avatar= image_tag avatar_url(current_user), alt: 'avatar', height: "32", class: 'img-responsive' - if current_user %li.dropdown %a.dropdown-toggle{ "data-toggle" => "dropdown", href: "#" } diff --git a/vendor/assets/javascripts/angular-busy.js b/vendor/assets/javascripts/angular-busy.js new file mode 100644 index 000000000..3d0d1784d --- /dev/null +++ b/vendor/assets/javascripts/angular-busy.js @@ -0,0 +1,116 @@ +/* ============================================================ + * angular-busy.js v3.0.2 + * https://github.com/cgross/angular-busy + * ============================================================ + * Copyright 2013 Chris Gross + + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ============================================================ */ + +angular.module('cgBusy',['ajoslin.promise-tracker']); + +angular.module('cgBusy').value('cgBusyTemplateName','angular-busy.html'); + +angular.module('cgBusy').directive('cgBusy',['promiseTracker','$compile','$templateCache','cgBusyTemplateName','$http', + function(promiseTracker,$compile,$templateCache,cgBusyTemplateName,$http){ + return { + restrict: 'A', + link: function(scope, element, attrs, fn) { + + var options = scope.$eval(attrs.cgBusy); + + if (typeof options === 'string'){ + options = {tracker:options}; + } + + if (typeof options === 'undefined' || typeof options.tracker === 'undefined'){ + throw new Error('Options for cgBusy directive must be provided (tracker option is required).'); + } + + if (!scope.$cgBusyTracker){ + scope.$cgBusyTracker = {}; + } + + scope.$cgBusyTracker[options.tracker] = promiseTracker(options.tracker); + + var position = element.css('position'); + if (position === 'static' || position === '' || typeof position === 'undefined'){ + element.css('position','relative'); + } + + var indicatorTemplateName = options.template ? options.template : cgBusyTemplateName; + + $http.get(indicatorTemplateName,{cache: $templateCache}).success(function(indicatorTemplate){ + + options.backdrop = typeof options.backdrop === 'undefined' ? true : options.backdrop; + var backdrop = options.backdrop ? '
' : ''; + + var template = ''; + var templateElement = $compile(template)(scope); + + angular.element(templateElement.children()[options.backdrop?1:0]) + .css('position','absolute') + .css('top',0) + .css('left',0) + .css('right',0) + .css('bottom',0); + + element.append(templateElement); + + }).error(function(data){ + throw new Error('Template specified for cgBusy ('+options.template+') could not be loaded. ' + data); + }); + } + }; + } +]); + + +angular.module("cgBusy").run(["$templateCache", function($templateCache) { + + $templateCache.put("angular-busy.html", + "
" + + "" + + "
" + + "" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "" + + "
Please Wait...
" + + "" + + "
" + + "" + + "
" + ); + +}]); + diff --git a/vendor/assets/javascripts/promise-tracker.js b/vendor/assets/javascripts/promise-tracker.js new file mode 100644 index 000000000..5aa155508 --- /dev/null +++ b/vendor/assets/javascripts/promise-tracker.js @@ -0,0 +1,242 @@ +/* + * promise-tracker - v1.2.0 - 2013-04-24 + * http://github.com/ajoslin/angular-promise-tracker + * Created by Andy Joslin; Licensed under Public Domain + */ +angular.module('ajoslin.promise-tracker', []) + +.config(function($httpProvider) { + if ($httpProvider.interceptors) { + //Support angularJS 1.1.4: interceptors + $httpProvider.interceptors.push('trackerHttpInterceptor'); + } else { + //Support angularJS pre 1.1.4: responseInterceptors + $httpProvider.responseInterceptors.push('trackerResponseInterceptor'); + } +}) +; + + +angular.module('ajoslin.promise-tracker') + +/* + * Intercept all http requests that have a `tracker` option in their config, + * and add that http promise to the specified `tracker` + */ + +//angular versions before 1.1.4 use responseInterceptor format +.factory('trackerResponseInterceptor', function($q, promiseTracker, $injector) { + //We use $injector get around circular dependency problem for $http + var $http; + return function spinnerResponseInterceptor(promise) { + if (!$http) $http = $injector.get('$http'); //lazy-load http + //We know the latest request is always going to be last in the list + var config = $http.pendingRequests[$http.pendingRequests.length-1]; + if (config.tracker) { + promiseTracker(config.tracker).addPromise(promise, config); + } + return promise; + }; +}) + +.factory('trackerHttpInterceptor', function($q, promiseTracker, $injector) { + return { + request: function(config) { + if (config.tracker) { + var deferred = promiseTracker(config.tracker).createPromise(config); + config.$promiseTrackerDeferred = deferred; + } + return $q.when(config); + }, + response: function(response) { + if (response.config.$promiseTrackerDeferred) { + response.config.$promiseTrackerDeferred.resolve(response); + } + return $q.when(response); + }, + responseError: function(response) { + if (response.config.$promiseTrackerDeferred) { + response.config.$promiseTrackerDeferred.reject(response); + } + return $q.reject(response); + } + }; +}) + +; + + +angular.module('ajoslin.promise-tracker') + +.provider('promiseTracker', function() { + var trackers = {}; + + this.$get = ['$q', '$timeout', function($q, $timeout) { + var self = this; + + function Tracker(options) { + options = options || {}; + var self = this, + //Define our callback types. The user can catch when a promise starts, + //has an error, is successful, or just is done with error or success. + callbacks = { + start: [], //Start is called when a new promise is added + done: [], //Called when a promise is resolved (error or success) + error: [], //Called on error. + success: [] //Called on success. + }, + trackedPromises = []; + + //Allow an optional "minimum duration" that the tracker has to stay + //active for. For example, if minimum duration is 1000ms and the user + //adds three promises that all resolve after 650ms, the tracker will + //still count itself as active until 1000ms have passed. + self.setMinDuration = function(minimum) { + self._minDuration = minimum; + }; + self.setMinDuration(options.minDuration); + + //Allow an option "maximum duration" that the tracker can stay active. + //Ideally, the user would resolve his promises after a certain time to + //achieve this 'maximum duration' option, but there are a few cases + //where it is necessary anyway. + self.setMaxDuration = function(maximum) { + self._maxDuration = maximum; + }; + self.setMaxDuration(options.maxDuration); + + //## active() + //Returns whether the promiseTracker is active - detect if we're + //currently tracking any promises. + self.active = function() { + return trackedPromises.length > 0; + }; + + //## cancel() + //Resolves all the current promises, immediately ending the tracker. + self.cancel = function() { + angular.forEach(trackedPromises, function(deferred) { + deferred.resolve(); + }); + }; + + function fireEvent(event, param) { + angular.forEach(callbacks[event], function(cb) { + cb.call(self, param); + }); + } + + //Create a promise that will make our tracker active until it is resolved. + //@param startArg: params to pass to 'start' event + //@return deferred - our deferred object that is being tracked + function createPromise(startArg) { + //We create our own promise to track. This usually piggybacks on a given + //promise, or we give it back and someone else can resolve it (like + //with the httpResponseInterceptor). + //Using our own promise also lets us do things like cancel early or add + //a minimum duration. + var deferred = $q.defer(); + + trackedPromises.push(deferred); + fireEvent('start', startArg); + + //If the tracker was just inactive and this the first in the list of + //promises, we reset our 'minimum duration' and 'maximum duration' + //again. + if (trackedPromises.length === 1) { + if (self._minDuration) { + self.minPromise = $timeout(angular.noop, self._minDuration); + } else { + //No minDuration means we just instantly resolve for our 'wait' + //promise. + self.minPromise = $q.when(true); + } + if (self._maxDuration) { + self.maxPromise = $timeout(deferred.resolve, self._maxDuration); + } + } + + //Create a callback for when this promise is done. It will remove our + //tracked promise from the array and call the appropriate event + //callbacks depending on whether there was an error or not. + function onDone(isError) { + return function(value) { + //Before resolving our promise, make sure the minDuration timeout + //has finished. + self.minPromise.then(function() { + fireEvent('done', value); + fireEvent(isError ? 'error' : 'success', value); + var index = trackedPromises.indexOf(deferred); + trackedPromises.splice(index, 1); + + //If this is the last promise, cleanup the timeout + //for maxDuration so it doesn't stick around. + if (trackedPromises.length === 0 && self.maxPromise) { + $timeout.cancel(self.maxPromise); + } + }); + }; + } + + deferred.promise.then(onDone(false), onDone(true)); + + return deferred; + } + + //## addPromise() + //Adds a given promise to our tracking + self.addPromise = function(promise, startArg) { + var deferred = createPromise(startArg); + + //When given promise is done, resolve our created promise + promise.then(function success(value) { + deferred.resolve(value); + return value; + }, function error(value) { + deferred.reject(value); + return $q.reject(value); + }); + + return deferred; + }; + + //## createPromise() + //Create a new promise and return it, and let the user resolve it how + //they see fit. + self.createPromise = function(startArg) { + return createPromise(startArg); + }; + + //## on(), bind() + self.on = self.bind = function(event, cb) { + if (!callbacks[event]) { + throw "Cannot create callback for event '" + event + + "'. Allowed types: 'start', 'done', 'error', 'success'"; + } + callbacks[event].push(cb); + return self; + }; + self.off = self.unbind = function(event, cb) { + if (!callbacks[event]) { + throw "Cannot create callback for event '" + event + + "'. Allowed types: 'start', 'done', 'error', 'success'"; + } + if (cb) { + var index = callbacks[event].indexOf(cb); + callbacks[event].splice(index, 1); + } else { + //Erase all events of this type if no cb specified to remvoe + callbacks[event].length = 0; + } + return self; + }; + } + return function promiseTracker(trackerName, options) { + if (!trackers[trackerName]) { + trackers[trackerName] = new Tracker(options); + } + return trackers[trackerName]; + }; + }]; +}) +; diff --git a/vendor/assets/stylesheets/angular-busy.css b/vendor/assets/stylesheets/angular-busy.css new file mode 100644 index 000000000..f82547317 --- /dev/null +++ b/vendor/assets/stylesheets/angular-busy.css @@ -0,0 +1,295 @@ +/* ============================================================ + * angular-busy.css v3.0.2 + * https://github.com/cgross/angular-busy + * ============================================================ + * Copyright 2013 Chris Gross + + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ============================================================ */ + +.cg-busy{ + position:absolute; + top:0px; + left:0px; + right:0px; + bottom:0px; +} + +.cg-busy-animation.ng-hide-add, +.cg-busy-animation.ng-hide-remove { + -webkit-transition:all .3s ease; + -moz-transition:all .3s ease; + -o-transition:all .3s ease; + transition:all .3s ease; + display:block !important; +} +.cg-busy-animation.ng-hide-remove { + opacity:0; + -webkit-transform:translate(0px,-40px); + -moz-transform:translate(0px,-40px); + -ms-transform:translate(0px,-40px); + -o-transform:translate(0px,-40px); + transform:translate(0px,-40px); +} +.cg-busy-animation.ng-hide-remove.ng-hide-remove-active { + opacity:1; + -webkit-transform:translate(0px,0px); + -moz-transform:translate(0px,0px); + -ms-transform:translate(0px,0px); + -o-transform:translate(0px,0px); + transform:translate(0px,0px); +} +.cg-busy-animation.ng-hide-add { + opacity:1; + -webkit-transform:translate(0px,0px); + -moz-transform:translate(0px,0px); + -ms-transform:translate(0px,0px); + -o-transform:translate(0px,0px); + transform:translate(0px,0px); +} +.cg-busy-animation.ng-hide-add.ng-hide-add-active { + opacity:0; + -webkit-transform:translate(0px,-40px); + -moz-transform:translate(0px,-40px); + -ms-transform:translate(0px,-40px); + -o-transform:translate(0px,-40px); + transform:translate(0px,-40px); +} + +.cg-busy-backdrop { + background-color:white; + opacity:.7; +} + +/* All styles below are for the default template. */ + +.cg-busy-default-sign{ + position:absolute; + top:0; + left:50%; + margin-left:-100px; + width:170px; + height:50px; + color:#333333; + text-shadow:0 1px 1px rgba(255, 255, 255, 0.75); + background-color:#e9eeee; + border:1px solid #dddddd; + border-top-width:0; + -webkit-border-radius:7px; + -moz-border-radius:7px; + border-radius:7px; + border-top-left-radius:0; + border-top-right-radius:0; + -webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); +} +.cg-busy-default-text{ + position:absolute; + left:49px; + top:15px; + font-size:16px; + color:#555; +} +.cg-busy-default-spinner{ + position:absolute; + width:25px; + height:25px; + display:inline-block; + top:12px; + left:14px; +} +.cg-busy-default-spinner div{ + width:12%; + height:26%; + background:#000; + position:absolute; + left:44.5%; + top:37%; + opacity:0; + -webkit-animation:cg-busy-spinner-anim 1s linear infinite; + -moz-animation:cg-busy-spinner-anim 1s linear infinite; + -ms-animation:cg-busy-spinner-anim 1s linear infinite; + -o-animation:cg-busy-spinner-anim 1s linear infinite; + animation:cg-busy-spinner-anim 1s linear infinite; + -webkit-border-radius:50px; + -moz-border-radius:50px; + border-radius:50px; + -webkit-box-shadow:0 0 3px rgba(0,0,0,0.2); + -moz-box-shadow:0 0 3px rgba(0,0,0,0.2); + box-shadow:0 0 3px rgba(0,0,0,0.2); +} +.cg-busy-default-spinner div.bar1{ + -webkit-transform:rotate(0deg) translate(0, -142%); + -moz-transform:rotate(0deg) translate(0, -142%); + -ms-transform:rotate(0deg) translate(0, -142%); + -o-transform:rotate(0deg) translate(0, -142%); + transform:rotate(0deg) translate(0, -142%); + -webkit-animation-delay:0s; + -moz-animation-delay:0s; + -ms-animation-delay:0s; + -o-animation-delay:0s; + animation-delay:0s; +} +.cg-busy-default-spinner div.bar2{ + -webkit-transform:rotate(30deg) translate(0, -142%); + -moz-transform:rotate(30deg) translate(0, -142%); + -ms-transform:rotate(30deg) translate(0, -142%); + -o-transform:rotate(30deg) translate(0, -142%); + transform:rotate(30deg) translate(0, -142%); + -webkit-animation-delay:-0.9167s; + -moz-animation-delay:-0.9167s; + -ms-animation-delay:-0.9167s; + -o-animation-delay:-0.9167s; + animation-delay:-0.9167s; +} +.cg-busy-default-spinner div.bar3{ + -webkit-transform:rotate(60deg) translate(0, -142%); + -moz-transform:rotate(60deg) translate(0, -142%); + -ms-transform:rotate(60deg) translate(0, -142%); + -o-transform:rotate(60deg) translate(0, -142%); + transform:rotate(60deg) translate(0, -142%); + -webkit-animation-delay:-0.833s; + -moz-animation-delay:-0.833s; + -ms-animation-delay:-0.833s; + -o-animation-delay:-0.833s; + animation-delay:-0.833s; +} +.cg-busy-default-spinner div.bar4{ + -webkit-transform:rotate(90deg) translate(0, -142%); + -moz-transform:rotate(90deg) translate(0, -142%); + -ms-transform:rotate(90deg) translate(0, -142%); + -o-transform:rotate(90deg) translate(0, -142%); + transform:rotate(90deg) translate(0, -142%); + -webkit-animation-delay:-0.75s; + -moz-animation-delay:-0.75s; + -ms-animation-delay:-0.75s; + -o-animation-delay:-0.75s; + animation-delay:-0.75s; +} +.cg-busy-default-spinner div.bar5{ + -webkit-transform:rotate(120deg) translate(0, -142%); + -moz-transform:rotate(120deg) translate(0, -142%); + -ms-transform:rotate(120deg) translate(0, -142%); + -o-transform:rotate(120deg) translate(0, -142%); + transform:rotate(120deg) translate(0, -142%); + -webkit-animation-delay:-0.667s; + -moz-animation-delay:-0.667s; + -ms-animation-delay:-0.667s; + -o-animation-delay:-0.667s; + animation-delay:-0.667s; +} +.cg-busy-default-spinner div.bar6{ + -webkit-transform:rotate(150deg) translate(0, -142%); + -moz-transform:rotate(150deg) translate(0, -142%); + -ms-transform:rotate(150deg) translate(0, -142%); + -o-transform:rotate(150deg) translate(0, -142%); + transform:rotate(150deg) translate(0, -142%); + -webkit-animation-delay:-0.5833s; + -moz-animation-delay:-0.5833s; + -ms-animation-delay:-0.5833s; + -o-animation-delay:-0.5833s; + animation-delay:-0.5833s; +} +.cg-busy-default-spinner div.bar7{ + -webkit-transform:rotate(180deg) translate(0, -142%); + -moz-transform:rotate(180deg) translate(0, -142%); + -ms-transform:rotate(180deg) translate(0, -142%); + -o-transform:rotate(180deg) translate(0, -142%); + transform:rotate(180deg) translate(0, -142%); + -webkit-animation-delay:-0.5s; + -moz-animation-delay:-0.5s; + -ms-animation-delay:-0.5s; + -o-animation-delay:-0.5s; + animation-delay:-0.5s; +} +.cg-busy-default-spinner div.bar8{ + -webkit-transform:rotate(210deg) translate(0, -142%); + -moz-transform:rotate(210deg) translate(0, -142%); + -ms-transform:rotate(210deg) translate(0, -142%); + -o-transform:rotate(210deg) translate(0, -142%); + transform:rotate(210deg) translate(0, -142%); + -webkit-animation-delay:-0.41667s; + -moz-animation-delay:-0.41667s; + -ms-animation-delay:-0.41667s; + -o-animation-delay:-0.41667s; + animation-delay:-0.41667s; +} +.cg-busy-default-spinner div.bar9{ + -webkit-transform:rotate(240deg) translate(0, -142%); + -moz-transform:rotate(240deg) translate(0, -142%); + -ms-transform:rotate(240deg) translate(0, -142%); + -o-transform:rotate(240deg) translate(0, -142%); + transform:rotate(240deg) translate(0, -142%); + -webkit-animation-delay:-0.333s; + -moz-animation-delay:-0.333s; + -ms-animation-delay:-0.333s; + -o-animation-delay:-0.333s; + animation-delay:-0.333s; +} +.cg-busy-default-spinner div.bar10{ + -webkit-transform:rotate(270deg) translate(0, -142%); + -moz-transform:rotate(270deg) translate(0, -142%); + -ms-transform:rotate(270deg) translate(0, -142%); + -o-transform:rotate(270deg) translate(0, -142%); + transform:rotate(270deg) translate(0, -142%); + -webkit-animation-delay:-0.25s; + -moz-animation-delay:-0.25s; + -ms-animation-delay:-0.25s; + -o-animation-delay:-0.25s; + animation-delay:-0.25s; +} +.cg-busy-default-spinner div.bar11{ + -webkit-transform:rotate(300deg) translate(0, -142%); + -moz-transform:rotate(300deg) translate(0, -142%); + -ms-transform:rotate(300deg) translate(0, -142%); + -o-transform:rotate(300deg) translate(0, -142%); + transform:rotate(300deg) translate(0, -142%); + -webkit-animation-delay:-0.1667s; + -moz-animation-delay:-0.1667s; + -ms-animation-delay:-0.1667s; + -o-animation-delay:-0.1667s; + animation-delay:-0.1667s; +} +.cg-busy-default-spinner div.bar12{ + -webkit-transform:rotate(330deg) translate(0, -142%); + -moz-transform:rotate(330deg) translate(0, -142%); + -ms-transform:rotate(330deg) translate(0, -142%); + -o-transform:rotate(330deg) translate(0, -142%); + transform:rotate(330deg) translate(0, -142%); + -webkit-animation-delay:-0.0833s; + -moz-animation-delay:-0.0833s; + -ms-animation-delay:-0.0833s; + -o-animation-delay:-0.0833s; + animation-delay:-0.0833s; +} + +@-webkit-keyframes cg-busy-spinner-anim{ + from {opacity: 1;} + to {opacity: 0.25;} +} +@-moz-keyframes cg-busy-spinner-anim{ + from {opacity: 1;} + to {opacity: 0.25;} +} +@keyframes cg-busy-spinner-anim{ + from {opacity: 1;} + to {opacity: 0.25;} +}