Merge pull request #4614 from luisramos0/base_admin_js

Bring some basic js functions and dependencies from spree_backend
This commit is contained in:
Luis Ramos
2020-02-04 14:48:38 +00:00
committed by GitHub
13 changed files with 1038 additions and 17 deletions

View File

@@ -14,6 +14,8 @@
//= require jquery.powertip
//= require jquery.cookie
//= require jquery.jstree/jquery.jstree
//= require jquery.vAlign
//= require jquery.horizontalNav
//= require angular
//= require angular-resource
//= require angular-animate
@@ -25,8 +27,13 @@
//= require lodash.underscore.js
// spree
//= require spree
//= require admin/spree-select2
//= require admin/spree_backend
//= require modernizr
//= require spin
//= require jquery.adaptivemenu
//= require equalize
//= require css_browser_selector_dev
//= require responsive-tables
//= require admin/spree_paypal_express

View File

@@ -0,0 +1,228 @@
//= require_self
//= require admin/handlebar_extensions
//= require admin/variant_autocomplete
/**
This is a collection of javascript functions and whatnot
under the spree namespace that do stuff we find helpful.
Hopefully, this will evolve into a propper class.
**/
jQuery(function($) {
// Make main menu use full width
mainMenu = $('.fullwidth-menu')
if (typeof mainMenu.horizontalNav === 'function' )
mainMenu.horizontalNav({
tableDisplay: false,
responsiveDelay: 0
});
// Vertical align of checkbox fields
if (typeof $('.field.checkbox label').vAlign === 'function' )
$('.field.checkbox label').vAlign()
// if (typeof Spree !== 'undefined') {
// $('.main-menu-wrapper ul').AdaptiveMenu({
// text: "<a href='#'><i class='icon-chevron-down'></i> " + Spree.translations.more + "</a>",
// klass: "dropdown"
// });
// }
// Add some tips
if (typeof $('.with-tip').powerTip === 'function' ) {
$('.with-tip').powerTip({
smartPlacement: true,
fadeInTime: 50,
fadeOutTime: 50,
intentPollInterval: 300
});
$('.with-tip').on({
powerTipPreRender: function(){
$('#powerTip').addClass($(this).attr("data-action"));
$('#powerTip').addClass($(this).attr("data-tip-color"));
},
powerTipClose: function(){
$('#powerTip').removeClass($(this).attr("data-action"))
}
});
}
// Make flash messages dissapear
setTimeout('$(".flash").fadeOut()', 5000);
// Highlight hovered table column
$('table tbody tr td.actions a').hover(function(){
var tr = $(this).closest('tr');
var klass = 'highlight action-' + $(this).attr('data-action')
tr.addClass(klass)
tr.prev().addClass('before-' + klass);
}, function(){
var tr = $(this).closest('tr');
var klass = 'highlight action-' + $(this).attr('data-action')
tr.removeClass(klass)
tr.prev().removeClass('before-' + klass);
});
// Trunkate text in page_title that didn't fit
var truncate_elements = $('.truncate');
truncate_elements.each(function(){
$(this).trunk8();
});
$(window).resize(function (event) {
truncate_elements.each(function(){
$(this).trunk8();
})
});
// Make height of dt/dd elements the same
if (typeof $("dl").equalize === 'function' )
$("dl").equalize('outerHeight');
});
$.fn.visible = function(cond) { this[cond ? 'show' : 'hide' ]() };
// Overriding a broken function in Spree. Bug report at
// https://github.com/spree/spree/issues/4032
show_flash_error = function(message) {
error_div = $('.flash.error');
if (error_div.length > 0) {
error_div.html(message);
error_div.show();
} else {
if ($("#content .toolbar").length > 0) {
$("#content .toolbar").before('<div class="flash error">' + message + '</div>');
} else {
$("#progress").before('<div class="flash error">' + message + '</div>');
}
}
}
// Apply to individual radio button that makes another element visible when checked
$.fn.radioControlsVisibilityOfElement = function(dependentElementSelector){
if(!this.get(0)){ return }
showValue = this.get(0).value;
radioGroup = $("input[name='" + this.get(0).name + "']");
radioGroup.each(function(){
$(this).click(function(){
$(dependentElementSelector).visible(this.checked && this.value == showValue)
});
if(this.checked){ this.click() }
});
}
$(document).ready(function() {
if (typeof Spree !== 'undefined') {
handle_date_picker_fields = function(){
$('.datepicker').datepicker({
dateFormat: Spree.translations.date_picker,
dayNames: Spree.translations.abbr_day_names,
dayNamesMin: Spree.translations.abbr_day_names,
monthNames: Spree.translations.month_names,
prevText: Spree.translations.previous,
nextText: Spree.translations.next,
showOn: "focus"
});
// Correctly display range dates
$('.date-range-filter .datepicker-from').datepicker('option', 'onSelect', function(selectedDate) {
$(".date-range-filter .datepicker-to" ).datepicker( "option", "minDate", selectedDate );
});
$('.date-range-filter .datepicker-to').datepicker('option', 'onSelect', function(selectedDate) {
$(".date-range-filter .datepicker-from" ).datepicker( "option", "maxDate", selectedDate );
});
}
handle_date_picker_fields();
}
$(".observe_field").on('change', function() {
target = $(this).attr("data-update");
ajax_indicator = $(this).attr("data-ajax-indicator") || '#busy_indicator';
$(target).hide();
$(ajax_indicator).show();
$.ajax({ dataType: 'html',
url: $(this).attr("data-base-url")+encodeURIComponent($(this).val()),
type: 'get',
success: function(data){
$(target).html(data);
$(ajax_indicator).hide();
$(target).show();
}
});
});
$('.spree_add_fields').click(function() {
var target = $(this).data("target");
var new_table_row = $(target + ' tr:visible:last').clone();
var new_id = new Date().getTime();
new_table_row.find("input, select").each(function () {
var el = $(this);
el.val("");
if (typeof el.attr("id") !== 'undefined') el.attr("id", el.attr("id").replace(/\d+/, new_id))
if (typeof el.attr("name") !== 'undefined') el.attr("name", el.attr("name").replace(/\d+/, new_id))
})
// When cloning a new row, set the href of all icons to be an empty "#"
// This is so that clicking on them does not perform the actions for the
// duplicated row
new_table_row.find("a").each(function () {
var el = $(this);
el.attr('href', '#');
})
$(target).prepend(new_table_row);
})
// Fix sortable helper
var fixHelper = function(e, ui) {
ui.children().each(function() {
$(this).width($(this).width());
});
return ui;
};
$('table.sortable').ready(function(){
var td_count = $(this).find('tbody tr:first-child td').length
if (typeof $('table.sortable tbody').sortable !== 'function' )
return
$('table.sortable tbody').sortable(
{
handle: '.handle',
helper: fixHelper,
placeholder: 'ui-sortable-placeholder',
update: function(event, ui) {
$("#progress").show();
positions = {};
$.each($('table.sortable tbody tr'), function(position, obj){
reg = /spree_(\w+_?)+_(\d+)/;
parts = reg.exec($(obj).attr('id'));
if (parts) {
positions['positions['+parts[2]+']'] = position;
}
});
$.ajax({
type: 'POST',
dataType: 'script',
url: $(ui.item).closest("table.sortable").data("sortable-link"),
data: positions,
success: function(data){ $("#progress").hide(); }
});
},
start: function (event, ui) {
// Set correct height for placehoder (from dragged tr)
ui.placeholder.height(ui.item.height())
// Fix placeholder content to make it correct width
ui.placeholder.html("<td colspan='"+(td_count-1)+"'></td><td class='actions'></td>")
},
stop: function (event, ui) {
// Fix odd/even classes after reorder
$("table.sortable tr:even").removeClass("odd even").addClass("even");
$("table.sortable tr:odd").removeClass("odd even").addClass("odd");
}
});
});
});

View File

@@ -0,0 +1,27 @@
$(document).ready ->
opts =
lines: 11
length: 2
width: 3
radius: 9
corners: 1
rotate: 0
color: '#fff'
speed: 0.8
trail: 48
shadow: false
hwaccel: true
className: 'spinner'
zIndex: 2e9
top: 'auto'
left: 'auto'
target = document.getElementById("spinner")
$(document).ajaxStart ->
$("#progress").fadeIn()
spinner = new Spinner(opts).spin(target)
$(document).ajaxStop ->
$("#progress").fadeOut()

View File

@@ -0,0 +1,8 @@
//= require select2
jQuery(function($) {
// Make select beautiful
if (typeof $('select.select2').select2 === 'function' )
$('select.select2').select2({
allowClear: true
});
})

View File

@@ -16,22 +16,6 @@ $(document).ready(function() {
});
});
// Overriding a broken function in Spree. Bug report at
// https://github.com/spree/spree/issues/4032
show_flash_error = function(message) {
error_div = $('.flash.error');
if (error_div.length > 0) {
error_div.html(message);
error_div.show();
} else {
if ($("#content .toolbar").length > 0) {
$("#content .toolbar").before('<div class="flash error">' + message + '</div>');
} else {
$("#progress").before('<div class="flash error">' + message + '</div>');
}
}
}
$(document).ready(function(){
$('a.close').click(function(event){
event.preventDefault();

View File

@@ -14,7 +14,6 @@
*= require_self
*/
//************************************************************************//
//************************************************************************//
@import 'globals/variables';

View File

@@ -0,0 +1,38 @@
@import 'admin/globals/variables';
@import 'admin/globals/mixins';
#progress {
display: none;
position: fixed;
top: 0;
z-index: 1000;
opacity: 0.8;
width: 100%;
.wrapper {
@include border-radius(10px);
top: -10px;
position: absolute;
left: 50%;
width: 200px;
margin-left: -100px;
padding: 11px 0;
background-color: $color-3;
color: $color-1;
text-align: center;
}
#spinner {
position: absolute;
top: 10px;
left: 50%;
margin-left: -5px;
}
.progress-message {
font-size: 120%;
font-weight: $font-weight-bold;
margin-top: 20px;
text-transform: uppercase;
}
}

View File

@@ -0,0 +1,25 @@
// Make color very close to white
@function very-light($color, $adjust: 3){
@if type-of($adjust) == 'number' and $adjust > 0 {
@for $i from 0 through 100 {
@if lighten($color, $i) == white and ($i - $adjust) > $adjust {
@return lighten($color, $i - $adjust);
}
}
}
@else {
@debug "Please correct $adjust value. It should be number and larger then 0. Currently it is '#{type-of($adjust)}' with value '#{$adjust}'"
}
};
// Quick fix for dynamic variables missing in SASS
@function get-value($prop, $val, $search) {
$n1: index($prop, $search);
$n2: index($val, $search);
@if($n1) {
@return nth($val, $n1);
} @else {
@return nth($prop, $n2);
}
}

View File

@@ -0,0 +1,193 @@
@import 'admin/globals/functions';
@import 'admin/globals/variables';
@import 'admin/globals/mixins';
@import 'admin/shared/forms';
@import 'admin/plugins/font-awesome';
.select2-container {
&:hover .select2-choice, &.select2-container-active .select2-choice {
background-color: $color-sel-hover-bg !important;
border-color: $color-sel-hover-bg !important;
}
.select2-choice {
background-image: none !important;
background-color: $color-sel-bg;
border: none !important;
box-shadow: none !important;
@include border-radius($border-radius);
color: $color-1 !important;
font-size: 90%;
height: 31px;
line-height: inherit !important;
padding: 5px 15px 7px;
span {
display: block;
padding: 2px;
}
.select2-search-choice-close {
background-image: none !important;
font-size: 100% !important;
@extend .icon-remove;
@extend [class^="icon-"]:before;
margin-top: 2px;
}
}
&.select2-container-active {
.select2-choice {
box-shadow: none !important;
}
&.select2-dropdown-open .select2-choice div b {
@extend .icon-caret-up
}
}
}
.select2-drop {
border-color: $color-sel-hover-bg;
box-shadow: none !important;
z-index: 1000000;
&.select2-drop-above {
border-color: $color-sel-hover-bg;
}
}
.select2-search {
@extend .icon-search;
font-size: 100%;
color: darken($color-border, 15);
padding: 0 9px 0 0;
&:before {
@extend [class^="icon-"]:before;
position: absolute;
top: 13px;
left: 13px;
}
input {
@extend input[type="text"];
padding: 6px 0 6px 25px;
margin: 5px 0 0 5px;
font-family: $base-font-family;
font-size: 90%;
box-shadow: none;
background-image: none;
}
}
.select2-container .select2-choice .select2-arrow {
background-image: none;
background: transparent;
border: 0;
b {
padding-top: 7px;
display: block;
width: 100%;
height: 100%;
background: none;
font-family: FontAwesome;
font-weight: 200 !important;
&:before {
content: "\f0d7";
}
}
}
.select2-results {
padding-left: 0 !important;
li {
font-size: 85% !important;
&.select2-highlighted {
.select2-result-label {
&, h6 {
color: $color-1 !important;
}
}
}
.select2-result-label {
color: $color-body-text;
min-height: 22px;
clear: both;
overflow: auto;
}
&.select2-no-results, &.select2-searching {
padding: 5px;
background-color: transparent;
color: $color-body-text;
text-align: center;
font-weight: $font-weight-bold;
text-transform: uppercase;
}
}
.select2-highlighted {
background-color: $color-sel-bg;
}
}
.select2-container-multi {
&.select2-container-active, &.select2-dropdown-open {
.select2-choices {
border-color: $color-sel-hover-bg !important;
box-shadow: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.select2-choices {
@extend input[type="text"];
padding: 6px 3px 3px 3px;
box-shadow: none;
background-image: none !important;
.select2-search-choice {
@include border-radius($border-radius);
margin: 0 0 3px 3px;
background-image: none;
background-color: $color-sel-bg;
border: none;
box-shadow: none;
color: $color-1 !important;
font-size: 85%;
&:hover {
background-color: $color-sel-hover-bg;
}
.select2-search-choice-close {
background-image: none !important;
font-size: 85% !important;
@extend .icon-remove;
@extend [class^="icon-"]:before;
margin-left: 2px;
color: $color-1;
}
}
}
}
label .select2-container {
margin-top: -6px;
.select2-choice {
span {
text-transform: none;
font-weight: normal;
}
}
}

41
vendor/assets/javascripts/equalize.js vendored Normal file
View File

@@ -0,0 +1,41 @@
/**
* equalize.js
* Author & copyright (c) 2012: Tim Svensen
* Dual MIT & GPL license
*
* Page: http://tsvensen.github.com/equalize.js
* Repo: https://github.com/tsvensen/equalize.js/
*
* The jQuery plugin for equalizing the height or width of elements.
*
* Equalize will accept any of the jQuery Dimension methods:
* height, outerHeight, innerHeight,
* width, outerWidth, innerWidth.
*
* EXAMPLE
* $('.parent').equalize(); // defaults to 'height'
* $('.parent').equalize('width'); // equalize the widths
*/
(function($, window, document, undefined) {
$.fn.equalize = function(equalize) {
var $containers = this, // this is the jQuery object
equalize = equalize || 'height',
type = (equalize.indexOf('eight') > 0) ? 'height' : 'width';
if (!$.isFunction($.fn[equalize])) { return false; }
return $containers.each(function() {
var $children = $(this).children(),
max = 0; // reset for each container
$children.each(function() {
var value = $(this)[equalize](); // call height(), outerHeight(), etc.
if (value > max) { max = value; } // update max
});
$children.css(type, max +'px'); // add CSS to children
});
};
}(jQuery, window, document));

View File

@@ -0,0 +1,141 @@
/**
* jQuery Horizontal Navigation 1.0
* https://github.com/sebnitu/horizontalNav
*
* By Sebastian Nitu - Copyright 2012 - All rights reserved
* Author URL: http://sebnitu.com
*/
(function($) {
$.fn.horizontalNav = function(options) {
// Extend our default options with those provided.
var opts = $.extend({}, $.fn.horizontalNav.defaults, options);
return this.each(function () {
// Save our object
var $this = $(this);
// Build element specific options
// This lets me access options with this syntax: o.optionName
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
// Save the wrapper. The wrapper is the element that
// we figure out what the full width should be
if ($this.is('ul')) {
var ul_wrap = $this.parent();
} else {
var ul_wrap = $this;
}
// let's append a clearfixing element to the ul wrapper
ul_wrap.css({ 'zoom' : '1' }).append('<div class="clearHorizontalNav">');
$('.clearHorizontalNav').css({
'display' : 'block',
'overflow' : 'hidden',
'visibility' : 'hidden',
'width' : 0,
'height' : 0,
'clear' : 'both'
});
// Grab elements we'll need and add some default styles
var ul = $this.is('ul') ? $this : ul_wrap.find('> ul'), // The unordered list element
li = ul.find('> li'), // All list items
li_last = li.last(), // Last list item
li_count = li.size(), // The number of navigation elements
li_a = li.find('> a'); // Remove padding from the links
// If set to responsive, re-construct after every browser resize
if ( o.responsive === true ) {
// Only need to do this for IE7 and below
// or if we set tableDisplay to false
if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {
resizeTrigger( _construct, o.responsiveDelay );
}
}
// Initiate the plugin
_construct();
// Returns the true inner width of an element
// Essentially it's the inner width without padding.
function trueInnerWidth(element) {
return element.innerWidth() - (
parseInt(element.css('padding-left')) + parseInt(element.css('padding-right'))
);
}
// Call funcion on browser resize
function resizeTrigger(callback, delay) {
// Delay before function is called
delay = delay || 100;
// Call function on resize
var resizeTimer;
$(window).resize(function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
callback();
}, delay);
});
}
// The heavy lifting of this plugin. This is where we
// find and set the appropriate widths for list items
function _construct() {
if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {
// IE7 doesn't support the "display: table" method
// so we need to do it the hard way.
// Add some styles
ul.css({ 'float' : 'left' });
li.css({ 'float' : 'left', 'width' : 'auto' });
li_a.css({ 'padding-left' : 0, 'padding-right' : 0 });
// Grabbing widths and doing some math
var ul_width = trueInnerWidth(ul),
ul_width_outer = ul.outerWidth(true),
ul_width_extra = ul_width_outer - ul_width,
full_width = trueInnerWidth(ul_wrap),
extra_width = (full_width - ul_width_extra) - ul_width,
li_padding = Math.floor( extra_width / li_count );
// Cycle through the list items and give them widths
li.each(function(index) {
var li_width = trueInnerWidth( $(this) );
$(this).css({ 'width' : (li_width + li_padding) + 'px' });
});
// Get the leftover pixels after we set every itms width
var li_last_width = trueInnerWidth(li_last) + ( (full_width - ul_width_extra) - trueInnerWidth(ul) );
// I hate to do this but for some reason Firefox (v13.0) and IE are always
// one pixel off when rendering. So this is a quick fix for that.
if ($.browser.mozilla || $.browser.msie) {
li_last_width = li_last_width - 1;
}
// Add the leftovers to the last navigation item
li_last.css({ 'width' : li_last_width + 'px' });
} else {
// Every modern browser supports the "display: table" method
// so this is the best way to do it for them.
ul.css({ 'display' : 'table', 'float' : 'none', 'width' : '100%' });
li.css({ 'display' : 'table-cell', 'float' : 'none' });
}
}
}); // @end of return this.each()
};
$.fn.horizontalNav.defaults = {
responsive : true,
responsiveDelay : 100,
tableDisplay : true
};
})(jQuery);

View File

@@ -0,0 +1,11 @@
(function ($) {
// VERTICALLY ALIGN FUNCTION
$.fn.vAlign = function() {
return this.each(function(i){
var ah = $(this).height();
var ph = $(this).parent().height();
var mh = Math.ceil((ph-ah) / 2);
$(this).css('margin-top', mh);
});
};
})(jQuery);

319
vendor/assets/javascripts/spin.js vendored Normal file
View File

@@ -0,0 +1,319 @@
//fgnass.github.com/spin.js#v1.2.6
!function(window, document, undefined) {
/**
* Copyright (c) 2011 Felix Gnass [fgnass at neteye dot de]
* Licensed under the MIT license
*/
var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
, animations = {} /* Animation rules keyed by their name */
, useCssAnimations
/**
* 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<n; i++)
parent.appendChild(arguments[i])
return parent
}
/**
* Insert a new stylesheet to hold the @keyframe or VML rules.
*/
var sheet = function() {
var el = createEl('style', {type : 'text/css'})
ins(document.getElementsByTagName('head')[0], el)
return el.sheet || el.styleSheet
}()
/**
* Creates an opacity keyframe animation rule and returns its name.
* Since most mobile Webkits have timing issues with animation-delay,
* we create separate rules for each line/segment.
*/
function addAnimation(alpha, trail, i, lines) {
var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-')
, start = 0.01 + i/lines*100
, z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
, prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
, pre = prefix && '-'+prefix+'-' || ''
if (!animations[name]) {
sheet.insertRule(
'@' + pre + 'keyframes ' + name + '{' +
'0%{opacity:' + z + '}' +
start + '%{opacity:' + alpha + '}' +
(start+0.01) + '%{opacity:1}' +
(start+trail) % 100 + '%{opacity:' + alpha + '}' +
'100%{opacity:' + z + '}' +
'}', sheet.cssRules.length)
animations[name] = 1
}
return name
}
/**
* Tries various vendor prefixes and returns the first supported property.
**/
function vendor(el, prop) {
var s = el.style
, pp
, i
if(s[prop] !== undefined) return prop
prop = prop.charAt(0).toUpperCase() + prop.slice(1)
for(i=0; i<prefixes.length; i++) {
pp = prefixes[i]+prop
if(s[pp] !== undefined) return pp
}
}
/**
* Sets multiple style properties at once.
*/
function css(el, prop) {
for (var n in prop)
el.style[vendor(el, n)||n] = prop[n]
return el
}
/**
* Fills in default values.
*/
function merge(obj) {
for (var i=1; i < arguments.length; i++) {
var def = arguments[i]
for (var n in def)
if (obj[n] === undefined) obj[n] = def[n]
}
return obj
}
/**
* Returns the absolute page-offset of the given element.
*/
function pos(el) {
var o = { x:el.offsetLeft, y:el.offsetTop }
while((el = el.offsetParent))
o.x+=el.offsetLeft, o.y+=el.offsetTop
return o
}
var defaults = {
lines: 12, // The number of lines to draw
length: 7, // The length of each line
width: 5, // 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
speed: 1, // 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: 'spinner', // CSS class to assign to the element
top: 'auto', // center vertically
left: 'auto' // center horizontally
}
/** The constructor */
var Spinner = function Spinner(o) {
if (!this.spin) return new Spinner(o)
this.opts = merge(o || {}, Spinner.defaults, defaults)
}
Spinner.defaults = {}
merge(Spinner.prototype, {
spin: function(target) {
this.stop()
var self = this
, o = self.opts
, el = self.el = css(createEl(0, {className: o.className}), {position: 'relative', width: 0, zIndex: o.zIndex})
, mid = o.radius+o.length+o.width
, ep // element position
, tp // target position
if (target) {
target.insertBefore(el, target.firstChild||null)
tp = pos(target)
ep = pos(el)
css(el, {
left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 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('aria-role', 'progressbar')
self.lines(el, self.opts)
if (!useCssAnimations) {
// No CSS animation support, use setTimeout() instead
var i = 0
, fps = o.fps
, f = fps/o.speed
, ostep = (1-o.opacity) / (f*o.trail / 100)
, astep = f/o.lines
;(function anim() {
i++;
for (var s=o.lines; s; s--) {
var alpha = Math.max(1-(i+s*astep)%f * ostep, o.opacity)
self.opacity(el, o.lines-s, alpha, o)
}
self.timeout = self.el && setTimeout(anim, ~~(1000/fps))
})()
}
return self
},
stop: function() {
var el = this.el
if (el) {
clearTimeout(this.timeout)
if (el.parentNode) el.parentNode.removeChild(el)
this.el = undefined
}
return this
},
lines: function(el, o) {
var i = 0
, 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, i, 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
},
opacity: function(el, i, val) {
if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
}
})
/////////////////////////////////////////////////////////////////////////
// VML rendering for IE
/////////////////////////////////////////////////////////////////////////
/**
* Check and init VML support
*/
;(function() {
function vml(tag, attr) {
return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
}
var s = css(createEl('group'), {behavior: 'url(#default#VML)'})
if (!vendor(s, 'transform') && s.adj) {
// VML support detected. Insert CSS rule ...
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
}
}
}
else
useCssAnimations = vendor(s, 'animation')
})()
if (typeof define == 'function' && define.amd)
define(function() { return Spinner })
else
window.Spinner = Spinner
}(window, document)