From 89251a667b71d3e5c3f17880ec5e04bb7b991c45 Mon Sep 17 00:00:00 2001 From: Kevin Granger Date: Fri, 11 Oct 2013 18:26:12 +0200 Subject: [PATCH] // feedback button --- admin-dev/themes/default/css/admin-theme.css | 2 +- .../default/css/admin-theme/_ladda-theme.sass | 27 + .../default/css/admin-theme/_ladda.scss | 471 ++++++++++++++++++ .../template/controllers/login/content.tpl | 20 +- .../template/controllers/login/header.tpl | 4 +- controllers/admin/AdminLoginController.php | 2 + js/login.js | 25 +- js/vendor/ladda.js | 314 ++++++++++++ js/vendor/spin.js | 349 +++++++++++++ 9 files changed, 1198 insertions(+), 16 deletions(-) create mode 100755 admin-dev/themes/default/css/admin-theme/_ladda-theme.sass create mode 100755 admin-dev/themes/default/css/admin-theme/_ladda.scss create mode 100755 js/vendor/ladda.js create mode 100755 js/vendor/spin.js diff --git a/admin-dev/themes/default/css/admin-theme.css b/admin-dev/themes/default/css/admin-theme.css index dfa3a9d53..f23c67a78 100644 --- a/admin-dev/themes/default/css/admin-theme.css +++ b/admin-dev/themes/default/css/admin-theme.css @@ -7830,7 +7830,7 @@ a.link-social:hover { display: none; } /* line 24, admin-theme/_admin-nav.sass */ #nav-sidebar ul.menu { - margin: 0 0 50px 0; + margin: 0; padding: 0; border-top: 1px black solid; list-style: none; } diff --git a/admin-dev/themes/default/css/admin-theme/_ladda-theme.sass b/admin-dev/themes/default/css/admin-theme/_ladda-theme.sass new file mode 100755 index 000000000..f620efa15 --- /dev/null +++ b/admin-dev/themes/default/css/admin-theme/_ladda-theme.sass @@ -0,0 +1,27 @@ +$green: #2aca76 +$blue: #53b5e6 +$red: #ea8557 +$purple: #9973C2 +$mint: #16a085 + +.ladda-button + &[disabled], + &[data-loading] + border-color: rgba( 0, 0, 0, 0.07 ) + cursor: default + background-color: #999 + &:hover + cursor: default + background-color: #999 + &[data-size=xs] + padding: 4px 8px + .ladda-label + font-size: 0.7em + &[data-size=s] + padding: 6px 10px + .ladda-label + font-size: 0.9em + &[data-size=l] .ladda-label + font-size: 1.2em + &[data-size=xl] .ladda-label + font-size: 1.5em \ No newline at end of file diff --git a/admin-dev/themes/default/css/admin-theme/_ladda.scss b/admin-dev/themes/default/css/admin-theme/_ladda.scss new file mode 100755 index 000000000..c9e24d36f --- /dev/null +++ b/admin-dev/themes/default/css/admin-theme/_ladda.scss @@ -0,0 +1,471 @@ +/*! + * Ladda + * http://lab.hakim.se/ladda + * MIT licensed + * + * Copyright (C) 2013 Hakim El Hattab, http://hakim.se + */ + + +/************************************* + * CONFIG + */ + +$spinnerSize: 32px; + + +/************************************* + * MIXINS + */ + +@mixin prefix ( $property, $value ) { + -webkit-#{$property}: $value; + -moz-#{$property}: $value; + -ms-#{$property}: $value; + -o-#{$property}: $value; + #{$property}: $value; +} + +@mixin transition( $value ) { + -webkit-transition: $value !important; // important to override bootstrap + -moz-transition: $value !important; + -ms-transition: $value !important; + -o-transition: $value !important; + transition: $value !important; +} + +@mixin vendor-transform( $value ) { + @include prefix( transform, $value ); +} + +@mixin transform-origin( $value ) { + @include prefix( transform-origin, $value ); +} + +@mixin buttonColor( $name, $color ) { + &[data-color=#{$name}] { + background: $color; + + &:hover { + background-color: lighten( $color, 5% ); + } + } +} + + +/************************************* + * BUTTON BASE + */ + +.ladda-button { + position: relative; +} + + +/* Spinner animation */ +.ladda-button .ladda-spinner { + position: absolute; + z-index: 2; + display: inline-block; + width: $spinnerSize; + height: $spinnerSize; + top: 50%; + margin-top: -$spinnerSize/2; + opacity: 0; + pointer-events: none; +} + +/* Button label */ +.ladda-button .ladda-label { + position: relative; + z-index: 3; +} + +/* Progress bar */ +.ladda-button .ladda-progress { + position: absolute; + width: 0; + height: 100%; + left: 0; + top: 0; + background: rgba( 0, 0, 0, 0.2 ); + + visibility: hidden; + opacity: 0; + + @include transition( 0.1s linear all ); +} + .ladda-button[data-loading] .ladda-progress { + opacity: 1; + visibility: visible; + } + + +/************************************* + * EASING + */ + +.ladda-button, +.ladda-button .ladda-spinner, +.ladda-button .ladda-label { + @include transition( 0.3s cubic-bezier(0.175, 0.885, 0.320, 1.275) all ); +} + +.ladda-button[data-style=zoom-in], +.ladda-button[data-style=zoom-in] .ladda-spinner, +.ladda-button[data-style=zoom-in] .ladda-label, +.ladda-button[data-style=zoom-out], +.ladda-button[data-style=zoom-out] .ladda-spinner, +.ladda-button[data-style=zoom-out] .ladda-label { + @include transition( 0.3s ease all ); +} + + +/************************************* + * EXPAND LEFT + */ + +.ladda-button[data-style=expand-right] { + .ladda-spinner { + right: 14px; + } + + &[data-size="s"] .ladda-spinner, + &[data-size="xs"] .ladda-spinner { + right: 4px; + } + + &[data-loading] { + padding-right: 56px; + + .ladda-spinner { + opacity: 1; + } + + &[data-size="s"], + &[data-size="xs"] { + padding-right: 40px; + } + } +} + + +/************************************* + * EXPAND RIGHT + */ + +.ladda-button[data-style=expand-left] { + .ladda-spinner { + left: 14px; + } + + &[data-size="s"] .ladda-spinner, + &[data-size="xs"] .ladda-spinner { + left: 4px; + } + + &[data-loading] { + padding-left: 56px; + + .ladda-spinner { + opacity: 1; + } + + &[data-size="s"], + &[data-size="xs"] { + padding-left: 40px; + } + } +} + + +/************************************* + * EXPAND UP + */ + +.ladda-button[data-style=expand-up] { + overflow: hidden; + + .ladda-spinner { + top: -$spinnerSize; + left: 50%; + margin-left: -$spinnerSize/2; + } + + &[data-loading] { + padding-top: 54px; + + .ladda-spinner { + opacity: 1; + top: 14px; + margin-top: 0; + } + + &[data-size="s"], + &[data-size="xs"] { + padding-top: 32px; + + .ladda-spinner { + top: 4px; + } + } + } +} + + +/************************************* + * EXPAND DOWN + */ + + .ladda-button[data-style=expand-down] { + overflow: hidden; + + .ladda-spinner { + top: 62px; + left: 50%; + margin-left: -$spinnerSize/2; + } + + &[data-size="s"] .ladda-spinner, + &[data-size="xs"] .ladda-spinner { + top: 40px; + } + + &[data-loading] { + padding-bottom: 54px; + + .ladda-spinner { + opacity: 1; + } + + &[data-size="s"], + &[data-size="xs"] { + padding-bottom: 32px; + } + } + } + + +/************************************* + * SLIDE LEFT + */ +.ladda-button[data-style=slide-left] { + overflow: hidden; + + .ladda-label { + position: relative; + } + .ladda-spinner { + left: 100%; + margin-left: -$spinnerSize/2; + } + + &[data-loading] { + .ladda-label { + opacity: 0; + left: -100%; + } + .ladda-spinner { + opacity: 1; + left: 50%; + } + } +} + + +/************************************* + * SLIDE RIGHT + */ +.ladda-button[data-style=slide-right] { + overflow: hidden; + + .ladda-label { + position: relative; + } + .ladda-spinner { + right: 100%; + margin-left: -$spinnerSize/2; + } + + &[data-loading] { + .ladda-label { + opacity: 0; + left: 100%; + } + .ladda-spinner { + opacity: 1; + left: 50%; + } + } +} + + +/************************************* + * SLIDE UP + */ +.ladda-button[data-style=slide-up] { + overflow: hidden; + + .ladda-label { + position: relative; + } + .ladda-spinner { + left: 50%; + margin-left: -$spinnerSize/2; + margin-top: 1em; + } + + &[data-loading] { + .ladda-label { + opacity: 0; + top: -1em; + } + .ladda-spinner { + opacity: 1; + margin-top: -$spinnerSize/2; + } + } +} + + +/************************************* + * SLIDE DOWN + */ +.ladda-button[data-style=slide-down] { + overflow: hidden; + + .ladda-label { + position: relative; + } + .ladda-spinner { + left: 50%; + margin-left: -$spinnerSize/2; + margin-top: -2em; + } + + &[data-loading] { + .ladda-label { + opacity: 0; + top: 1em; + } + .ladda-spinner { + opacity: 1; + margin-top: -$spinnerSize/2; + } + } +} + + +/************************************* + * ZOOM-OUT + */ + +.ladda-button[data-style=zoom-out] { + overflow: hidden; +} + .ladda-button[data-style=zoom-out] .ladda-spinner { + left: 50%; + margin-left: -$spinnerSize/2; + + @include vendor-transform( scale( 2.5 ) ); + } + .ladda-button[data-style=zoom-out] .ladda-label { + position: relative; + display: inline-block; + } + +.ladda-button[data-style=zoom-out][data-loading] .ladda-label { + opacity: 0; + + @include vendor-transform( scale( 0.5 ) ); +} +.ladda-button[data-style=zoom-out][data-loading] .ladda-spinner { + opacity: 1; + + @include vendor-transform( none ); +} + + +/************************************* + * ZOOM-IN + */ + +.ladda-button[data-style=zoom-in] { + overflow: hidden; +} + .ladda-button[data-style=zoom-in] .ladda-spinner { + left: 50%; + margin-left: -$spinnerSize/2; + + @include vendor-transform( scale( 0.2 ) ); + } + .ladda-button[data-style=zoom-in] .ladda-label { + position: relative; + display: inline-block; + } + +.ladda-button[data-style=zoom-in][data-loading] .ladda-label { + opacity: 0; + + @include vendor-transform( scale( 2.2 ) ); +} +.ladda-button[data-style=zoom-in][data-loading] .ladda-spinner { + opacity: 1; + + @include vendor-transform( none ); +} + + +/************************************* + * CONTRACT + */ + +.ladda-button[data-style=contract] { + overflow: hidden; + width: 100px; +} + .ladda-button[data-style=contract] .ladda-spinner { + left: 50%; + margin-left: -16px; + } + +.ladda-button[data-style=contract][data-loading] { + border-radius: 50%; + width: 52px; +} + .ladda-button[data-style=contract][data-loading] .ladda-label { + opacity: 0; + } + .ladda-button[data-style=contract][data-loading] .ladda-spinner { + opacity: 1; + } + + + +/************************************* + * OVERLAY + */ + +.ladda-button[data-style=contract-overlay] { + overflow: hidden; + width: 100px; + box-shadow: 0px 0px 0px 3000px rgba(0,0,0,0); +} + .ladda-button[data-style=contract-overlay] .ladda-spinner { + left: 50%; + margin-left: -16px; + } + +.ladda-button[data-style=contract-overlay][data-loading] { + border-radius: 50%; + width: 52px; + + /*outline: 10000px solid rgba( 0, 0, 0, 0.5 );*/ + box-shadow: 0px 0px 0px 3000px rgba(0,0,0,0.8); +} + .ladda-button[data-style=contract-overlay][data-loading] .ladda-label { + opacity: 0; + } + .ladda-button[data-style=contract-overlay][data-loading] .ladda-spinner { + opacity: 1; + } diff --git a/admin-dev/themes/default/template/controllers/login/content.tpl b/admin-dev/themes/default/template/controllers/login/content.tpl index 99a9f3ca6..941033a2c 100755 --- a/admin-dev/themes/default/template/controllers/login/content.tpl +++ b/admin-dev/themes/default/template/controllers/login/content.tpl @@ -32,7 +32,11 @@
-

{$shop_name}

+

+ + PRESTASHOP +

+

{$shop_name}


{if isset($errors)} @@ -71,11 +75,11 @@
+ + {l s='Lost password'}… +
@@ -99,9 +103,11 @@

-
diff --git a/admin-dev/themes/default/template/controllers/login/header.tpl b/admin-dev/themes/default/template/controllers/login/header.tpl index d63071bf9..4aaad07fb 100644 --- a/admin-dev/themes/default/template/controllers/login/header.tpl +++ b/admin-dev/themes/default/template/controllers/login/header.tpl @@ -33,7 +33,9 @@ - {$shop_name} {if $meta_title != ''}{if isset($navigationPipe)}{$navigationPipe|escape:'htmlall':'UTF-8'}{else}>{/if} {$meta_title}{/if} (PrestaShop™) + + {$shop_name} {if $meta_title != ''}{if isset($navigationPipe)}{$navigationPipe|escape:'htmlall':'UTF-8'}{else}>{/if} {$meta_title}{/if} (PrestaShop™) + {foreach from=$css_files key=css_uri item=media} {/foreach} diff --git a/controllers/admin/AdminLoginController.php b/controllers/admin/AdminLoginController.php index 5a14a80e6..666f9185d 100755 --- a/controllers/admin/AdminLoginController.php +++ b/controllers/admin/AdminLoginController.php @@ -46,6 +46,8 @@ class AdminLoginControllerCore extends AdminController $this->addJquery(); $this->addCSS('/'.$admin_webpath.'/themes/'.$this->bo_theme.'/css/admin-theme.css'); $this->addJS(_PS_JS_DIR_.'vendor/jquery.validate.js'); + $this->addJS(_PS_JS_DIR_.'vendor/spin.js'); + $this->addJS(_PS_JS_DIR_.'vendor/ladda.js'); $this->addJS(_PS_JS_DIR_.'login.js'); $this->addJqueryUI('ui.widget'); $this->addJqueryUI('effects.shake'); diff --git a/js/login.js b/js/login.js index 2dbd8c26d..34a3bcaaf 100644 --- a/js/login.js +++ b/js/login.js @@ -1,11 +1,11 @@ $(document).ready(function() { // Initialize events $("#login_form").validate({ - rules: { - "email":{ + rules: { + "email":{ "email": true, "required": true }, - "passwd": { + "passwd": { "required": true } }, submitHandler: function(form) { @@ -64,9 +64,13 @@ $(document).ready(function() { } }); }); - }); +//todo: ladda init - move to top +var l = new Object(); +function feedbackSubmit() { + l = Ladda.create( document.querySelector( 'button[type=submit]' ) ); +} function displayForgotPassword() { $('#error').hide(); @@ -111,7 +115,12 @@ function doAjaxLogin(redirect) { redirect: redirect, stay_logged_in: $('#stay_logged_in:checked').val() }, + beforeSend: function(){ + feedbackSubmit(); + l.start(); + }, success: function(jsonData) { + l.stop(); if (jsonData.hasErrors) displayErrors(jsonData.errors); else @@ -163,7 +172,9 @@ function displayErrors(errors) { for (var error in errors) //IE6 bug fix if (error != 'indexOf') str_errors += '
  • ' + errors[error] + '
  • '; $('#error').html(str_errors + '').removeClass('hide').fadeIn('slow'); - $("#login").effect("shake", { - times: 4 - }, 100); + + + // $("#login").effect("shake", { + // times: 4 + // }, 100); } \ No newline at end of file diff --git a/js/vendor/ladda.js b/js/vendor/ladda.js new file mode 100755 index 000000000..e161f9430 --- /dev/null +++ b/js/vendor/ladda.js @@ -0,0 +1,314 @@ +/*! + * Ladda 0.8.0 + * http://lab.hakim.se/ladda + * MIT licensed + * + * Copyright (C) 2013 Hakim El Hattab, http://hakim.se + */ +(function( root, factory ) { + + // CommonJS + if( typeof exports === 'object' ) { + module.exports = factory(); + } + // AMD module + else if( typeof define === 'function' && define.amd ) { + define( [ 'spin' ], factory ); + } + // Browser global + else { + root.Ladda = factory( root.Spinner ); + } + +} +(this, function( Spinner ) { + 'use strict'; + + // All currently instantiated instances of Ladda + var ALL_INSTANCES = []; + + /** + * Creates a new instance of Ladda which wraps the + * target button element. + * + * @return An API object that can be used to control + * the loading animation state. + */ + function create( button ) { + + if( typeof button === 'undefined' ) { + console.warn( "Ladda button target must be defined." ); + return; + } + + // The text contents must be wrapped in a ladda-label + // element, create one if it doesn't already exist + if( !button.querySelector( '.ladda-label' ) ) { + button.innerHTML = ''+ button.innerHTML +''; + } + + // Create the spinner + var spinner = createSpinner( button ); + + // Wrapper element for the spinner + var spinnerWrapper = document.createElement( 'span' ); + spinnerWrapper.className = 'ladda-spinner'; + button.appendChild( spinnerWrapper ); + + // Timer used to delay starting/stopping + var timer; + + var instance = { + + /** + * Enter the loading state. + */ + start: function() { + + button.setAttribute( 'disabled', '' ); + button.setAttribute( 'data-loading', '' ); + + clearTimeout( timer ); + spinner.spin( spinnerWrapper ); + + this.setProgress( 0 ); + + return this; // chain + + }, + + /** + * Enter the loading state, after a delay. + */ + startAfter: function( delay ) { + + clearTimeout( timer ); + timer = setTimeout( function() { instance.start(); }, delay ); + + return this; // chain + + }, + + /** + * Exit the loading state. + */ + stop: function() { + + button.removeAttribute( 'disabled' ); + button.removeAttribute( 'data-loading' ); + + // Kill the animation after a delay to make sure it + // runs for the duration of the button transition + clearTimeout( timer ); + timer = setTimeout( function() { spinner.stop(); }, 1000 ); + + return this; // chain + + }, + + /** + * Toggle the loading state on/off. + */ + toggle: function() { + + if( this.isLoading() ) { + this.stop(); + } + else { + this.start(); + } + + return this; // chain + + }, + + /** + * Sets the width of the visual progress bar inside of + * this Ladda button + * + * @param {Number} progress in the range of 0-1 + */ + setProgress: function( progress ) { + + // Cap it + progress = Math.max( Math.min( progress, 1 ), 0 ); + + var progressElement = button.querySelector( '.ladda-progress' ); + + // Remove the progress bar if we're at 0 progress + if( progress === 0 && progressElement && progressElement.parentNode ) { + progressElement.parentNode.removeChild( progressElement ); + } + else { + if( !progressElement ) { + progressElement = document.createElement( 'div' ); + progressElement.className = 'ladda-progress'; + button.appendChild( progressElement ); + } + + progressElement.style.width = ( ( progress || 0 ) * button.offsetWidth ) + 'px'; + } + + }, + + enable: function() { + + this.stop(); + + return this; // chain + + }, + + disable: function () { + + this.stop(); + button.setAttribute( 'disabled', '' ); + + return this; // chain + + }, + + isLoading: function() { + + return button.hasAttribute( 'data-loading' ); + + } + + }; + + ALL_INSTANCES.push( instance ); + + return instance; + + } + + /** + * Binds the target buttons to automatically enter the + * loading state when clicked. + * + * @param target Either an HTML element or a CSS selector. + * @param options + * - timeout Number of milliseconds to wait before + * automatically cancelling the animation. + */ + function bind( target, options ) { + + options = options || {}; + + var targets = []; + + if( typeof target === 'string' ) { + targets = toArray( document.querySelectorAll( target ) ); + } + else if( typeof target === 'object' && typeof target.nodeName === 'string' ) { + targets = [ target ]; + } + + for( var i = 0, len = targets.length; i < len; i++ ) { + + (function() { + var element = targets[i]; + + // Make sure we're working with a DOM element + if( typeof element.addEventListener === 'function' ) { + var instance = create( element ); + var timeout = -1; + + element.addEventListener( 'click', function() { + + // This is asynchronous to avoid an issue where setting + // the disabled attribute on the button prevents forms + // from submitting + instance.startAfter( 1 ); + + // Set a loading timeout if one is specified + if( typeof options.timeout === 'number' ) { + clearTimeout( timeout ); + timeout = setTimeout( instance.stop, options.timeout ); + } + + // Invoke callbacks + if( typeof options.callback === 'function' ) { + options.callback.apply( null, [ instance ] ); + } + + }, false ); + } + })(); + + } + + } + + /** + * Stops ALL current loading animations. + */ + function stopAll() { + + for( var i = 0, len = ALL_INSTANCES.length; i < len; i++ ) { + ALL_INSTANCES[i].stop(); + } + + } + + function createSpinner( button ) { + + var height = button.offsetHeight, + spinnerColor; + + // If the button is tall we can afford some padding + if( height > 32 ) { + height *= 0.8; + } + + // Prefer an explicit height if one is defined + if( button.hasAttribute( 'data-spinner-size' ) ) { + height = parseInt( button.getAttribute( 'data-spinner-size' ), 10 ); + } + + // Allow buttons to specify the color of the spinner element + if (button.hasAttribute('data-spinner-color' ) ) { + spinnerColor = button.getAttribute( 'data-spinner-color' ); + } + + var lines = 12, + radius = height * 0.2, + length = radius * 0.6, + width = radius < 7 ? 2 : 3; + + return new Spinner( { + color: spinnerColor || '#fff', + lines: lines, + radius: radius, + length: length, + width: width, + zIndex: 'auto', + top: 'auto', + left: 'auto', + className: '' + } ); + + } + + function toArray( nodes ) { + + var a = []; + + for ( var i = 0; i < nodes.length; i++ ) { + a.push( nodes[ i ] ); + } + + return a; + + } + + // Public API + return { + + bind: bind, + create: create, + stopAll: stopAll + + }; + +})); diff --git a/js/vendor/spin.js b/js/vendor/spin.js new file mode 100755 index 000000000..7b1ca941f --- /dev/null +++ b/js/vendor/spin.js @@ -0,0 +1,349 @@ +//fgnass.github.com/spin.js#v1.3 + +/*! + * Copyright (c) 2011-2013 Felix Gnass + * Licensed under the MIT license + */ +(function(root, factory) { + + /* CommonJS */ + if (typeof exports == 'object') module.exports = factory() + + /* AMD module */ + else if (typeof define == 'function' && define.amd) define(factory) + + /* Browser global */ + else root.Spinner = factory() +} +(this, function() { + "use strict"; + + var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */ + , animations = {} /* Animation rules keyed by their name */ + , useCssAnimations /* Whether to use CSS animations or setTimeout */ + + /** + * Utility function to create elements. If no tag name is given, + * a DIV is created. Optionally properties can be passed. + */ + function createEl(tag, prop) { + var el = document.createElement(tag || 'div') + , n + + for(n in prop) el[n] = prop[n] + return el + } + + /** + * Appends children and returns the parent. + */ + function ins(parent /* child1, child2, ...*/) { + for (var i=1, n=arguments.length; i> 1) : parseInt(o.left, 10) + mid) + 'px', + top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : parseInt(o.top, 10) + mid) + 'px' + }) + } + + el.setAttribute('role', 'progressbar') + self.lines(el, self.opts) + + if (!useCssAnimations) { + // No CSS animation support, use setTimeout() instead + var i = 0 + , start = (o.lines - 1) * (1 - o.direction) / 2 + , alpha + , fps = o.fps + , f = fps/o.speed + , ostep = (1-o.opacity) / (f*o.trail / 100) + , astep = f/o.lines + + ;(function anim() { + i++; + for (var j = 0; j < o.lines; j++) { + alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity) + + self.opacity(el, j * o.direction + start, alpha, o) + } + self.timeout = self.el && setTimeout(anim, ~~(1000/fps)) + })() + } + return self + }, + + /** + * Stops and removes the Spinner. + */ + stop: function() { + var el = this.el + if (el) { + clearTimeout(this.timeout) + if (el.parentNode) el.parentNode.removeChild(el) + this.el = undefined + } + return this + }, + + /** + * Internal method that draws the individual lines. Will be overwritten + * in VML fallback mode below. + */ + lines: function(el, o) { + var i = 0 + , start = (o.lines - 1) * (1 - o.direction) / 2 + , seg + + function fill(color, shadow) { + return css(createEl(), { + position: 'absolute', + width: (o.length+o.width) + 'px', + height: o.width + 'px', + background: color, + boxShadow: shadow, + transformOrigin: 'left', + transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)', + borderRadius: (o.corners * o.width>>1) + 'px' + }) + } + + for (; i < o.lines; i++) { + seg = css(createEl(), { + position: 'absolute', + top: 1+~(o.width/2) + 'px', + transform: o.hwaccel ? 'translate3d(0,0,0)' : '', + opacity: o.opacity, + animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite' + }) + + if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'})) + + ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)'))) + } + return el + }, + + /** + * Internal method that adjusts the opacity of a single line. + * Will be overwritten in VML fallback mode below. + */ + opacity: function(el, i, val) { + if (i < el.childNodes.length) el.childNodes[i].style.opacity = val + } + + }) + + + function initVML() { + + /* Utility function to create a VML tag */ + function vml(tag, attr) { + return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr) + } + + // No CSS transforms but VML support, add a CSS rule for VML elements: + sheet.addRule('.spin-vml', 'behavior:url(#default#VML)') + + Spinner.prototype.lines = function(el, o) { + var r = o.length+o.width + , s = 2*r + + function grp() { + return css( + vml('group', { + coordsize: s + ' ' + s, + coordorigin: -r + ' ' + -r + }), + { width: s, height: s } + ) + } + + var margin = -(o.width+o.length)*2 + 'px' + , g = css(grp(), {position: 'absolute', top: margin, left: margin}) + , i + + function seg(i, dx, filter) { + ins(g, + ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}), + ins(css(vml('roundrect', {arcsize: o.corners}), { + width: r, + height: o.width, + left: o.radius, + top: -o.width>>1, + filter: filter + }), + vml('fill', {color: o.color, opacity: o.opacity}), + vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change + ) + ) + ) + } + + if (o.shadow) + for (i = 1; i <= o.lines; i++) + seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)') + + for (i = 1; i <= o.lines; i++) seg(i) + return ins(el, g) + } + + Spinner.prototype.opacity = function(el, i, val, o) { + var c = el.firstChild + o = o.shadow && o.lines || 0 + if (c && i+o < c.childNodes.length) { + c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild + if (c) c.opacity = val + } + } + } + + var probe = css(createEl('group'), {behavior: 'url(#default#VML)'}) + + if (!vendor(probe, 'transform') && probe.adj) initVML() + else useCssAnimations = vendor(probe, 'animation') + + return Spinner + +})); \ No newline at end of file