1/** 2 * Copyright (c) 2011-2014 Felix Gnass 3 * Licensed under the MIT license 4 * http://spin.js.org/ 5 * 6 * Example: 7 var opts = { 8 lines: 12, // The number of lines to draw 9 length: 7, // The length of each line 10 width: 5, // The line thickness 11 radius: 10, // The radius of the inner circle 12 scale: 1.0, // Scales overall size of the spinner 13 corners: 1, // Roundness (0..1) 14 color: '#000', // #rgb or #rrggbb 15 opacity: 1/4, // Opacity of the lines 16 rotate: 0, // Rotation offset 17 direction: 1, // 1: clockwise, -1: counterclockwise 18 speed: 1, // Rounds per second 19 trail: 100, // Afterglow percentage 20 fps: 20, // Frames per second when using setTimeout() 21 zIndex: 2e9, // Use a high z-index by default 22 className: 'spinner', // CSS class to assign to the element 23 top: '50%', // center vertically 24 left: '50%', // center horizontally 25 shadow: false, // Whether to render a shadow 26 hwaccel: false, // Whether to use hardware acceleration (might be buggy) 27 position: 'absolute' // Element positioning 28 }; 29 var target = document.getElementById('foo'); 30 var spinner = new Spinner(opts).spin(target); 31 */ 32;(function(root, factory) { 33 if (typeof module == 'object' && module.exports) module.exports = factory(); // CommonJS 34 else if (typeof define == 'function' && define.amd) define(factory); // AMD module 35 else root.Spinner = factory(); // Browser global 36} 37(this, function() { 38 'use strict'; 39 40 var prefixes = ['webkit', 'Moz', 'ms', 'O']; // Vendor prefixes 41 var animations = {}; // Animation rules keyed by their name 42 var useCssAnimations; // Whether to use CSS animations or setTimeout 43 var sheet; // A stylesheet to hold the @keyframe or VML rules 44 45 /** 46 * Utility function to create elements. If no tag name is given, 47 * a DIV is created. Optionally properties can be passed. 48 */ 49 function createEl(tag, prop) { 50 var el = document.createElement(tag || 'div'); 51 var n; 52 53 for (n in prop) el[n] = prop[n]; 54 return el; 55 } 56 57 /** 58 * Appends children and returns the parent. 59 */ 60 function ins(parent /* child1, child2, ...*/) { 61 for (var i = 1, n = arguments.length; i < n; i++) { 62 parent.appendChild(arguments[i]); 63 } 64 65 return parent; 66 } 67 68 /** 69 * Creates an opacity keyframe animation rule and returns its name. 70 * Since most mobile Webkits have timing issues with animation-delay, 71 * we create separate rules for each line/segment. 72 */ 73 function addAnimation(alpha, trail, i, lines) { 74 var name = ['opacity', trail, ~~(alpha * 100), i, lines].join('-'); 75 var start = 0.01 + i/lines * 100; 76 var z = Math.max(1 - (1-alpha) / trail * (100-start), alpha); 77 var prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase(); 78 var pre = prefix && '-' + prefix + '-' || ''; 79 80 if (!animations[name]) { 81 sheet.insertRule( 82 '@' + pre + 'keyframes ' + name + '{' + 83 '0%{opacity:' + z + '}' + 84 start + '%{opacity:' + alpha + '}' + 85 (start+0.01) + '%{opacity:1}' + 86 (start+trail) % 100 + '%{opacity:' + alpha + '}' + 87 '100%{opacity:' + z + '}' + 88 '}', sheet.cssRules.length); 89 90 animations[name] = 1; 91 } 92 93 return name; 94 } 95 96 /** 97 * Tries various vendor prefixes and returns the first supported property. 98 */ 99 function vendor(el, prop) { 100 var s = el.style; 101 var pp; 102 var i; 103 104 prop = prop.charAt(0).toUpperCase() + prop.slice(1); 105 if (s[prop] !== undefined) return prop; 106 for (i = 0; i < prefixes.length; i++) {
107 pp = prefixes[i]+prop; 108 if (s[pp] !== undefined) return pp; 109 } 110 } 111 112 /** 113 * Sets multiple style properties at once. 114 */ 115 function css(el, prop) { 116 for (var n in prop) { 117 el.style[vendor(el, n) || n] = prop[n]; 118 } 119 120 return el; 121 } 122 123 /** 124 * Fills in default values. 125 */ 126 function merge(obj) { 127 for (var i = 1; i < arguments.length; i++) { 128 var def = arguments[i]; 129 for (var n in def) { 130 if (obj[n] === undefined) obj[n] = def[n]; 131 } 132 } 133 return obj; 134 } 135 136 /** 137 * Returns the line color from the given string or array. 138 */ 139 function getColor(color, idx) { 140 return typeof color == 'string' ? color : color[idx % color.length]; 141 } 142 143 // Built-in defaults 144 145 var defaults = { 146 lines: 12, // The number of lines to draw 147 length: 7, // The length of each line 148 width: 5, // The line thickness 149 radius: 10, // The radius of the inner circle 150 scale: 1.0, // Scales overall size of the spinner 151 corners: 1, // Roundness (0..1) 152 color: '#000', // #rgb or #rrggbb 153 opacity: 1/4, // Opacity of the lines 154 rotate: 0, // Rotation offset 155 direction: 1, // 1: clockwise, -1: counterclockwise 156 speed: 1, // Rounds per second 157 trail: 100, // Afterglow percentage 158 fps: 20, // Frames per second when using setTimeout() 159 zIndex: 2e9, // Use a high z-index by default 160 className: 'spinner', // CSS class to assign to the element 161 top: '50%', // center vertically 162 left: '50%', // center horizontally 163 shadow: false, // Whether to render a shadow 164 hwaccel: false, // Whether to use hardware acceleration 165 position: 'fixed' // Element positioning 166 }; 167 168 /** The constructor */ 169 function Spinner(o) { 170 this.opts = merge(o || {}, Spinner.defaults, defaults); 171 } 172 173 // Global defaults that override the built-ins: 174 Spinner.defaults = {}; 175 176 merge(Spinner.prototype, { 177 /** 178 * Adds the spinner to the given target element. If this instance is already 179 * spinning, it is automatically removed from its previous target b calling 180 * stop() internally. 181 */ 182 spin: function(target) { 183 this.stop(); 184 185 var self = this; 186 var o = self.opts; 187 var el = self.el = createEl(null, {className: o.className}); 188 189 css(el, { 190 position: o.position, 191 width: 0, 192 zIndex: o.zIndex, 193 left: o.left, 194 top: o.top 195 }); 196 197 if (target) { 198 target.insertBefore(el, target.firstChild || null); 199 } 200 201 el.setAttribute('role', 'progressbar'); 202 self.lines(el, self.opts); 203 204 if (!useCssAnimations) { 205 // No CSS animation support, use setTimeout() instead 206 var i = 0; 207 var start = (o.lines - 1) * (1 - o.direction) / 2; 208 var alpha; 209 var fps = o.fps; 210 var f = fps / o.speed; 211 var ostep = (1 - o.opacity) / (f * o.trail / 100); 212 var astep = f / o.lines; 213 214 (function anim() { 215 i++; 216 for (var j = 0; j < o.lines; j++) { 217 alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity); 218 219 self.opacity(el, j * o.direction + start, alpha, o); 220 } 221 self.timeout = self.el && setTimeout(anim, ~~(1000 / fps)); 222 })(); 223 } 224 return self; 225 }, 226 227 /** 228 * Stops and removes the Spinner. 229 */ 230 stop: function() { 231 var el = this.el; 232 if (el) { 233 clearTimeout(this.timeout); 234 if (el.parentNode) el.parentNode.removeChild(el); 235 this.el = undefined; 236 } 237 return this; 238 }, 239 240 /** 241 * Internal method that draws the individual lines. Will be overwritten 242 * in VML fallback mode below. 243 */ 244 lines: function(el, o) { 245 var i = 0; 246 var start = (o.lines - 1) * (1 - o.direction) / 2; 247 var seg; 248 249 function fill(color, shadow) { 250 return css(createEl(), { 251 position: 'absolute', 252 width: o.scale * (o.length + o.width) + 'px', 253 height: o.scale * o.width + 'px', 254 background: color, 255 boxShadow: shadow, 256 transformOrigin: 'left', 257 transform: 'rotate(' + ~~(360/o.lines*i + o.rotate) + 'deg) translate(' + o.scale*o.radius + 'px' + ',0)', 258 borderRadius: (o.corners * o.scale * o.width >> 1) + 'px' 259 }); 260 } 261 262 for (; i < o.lines; i++) { 263 seg = css(createEl(), { 264 position: 'absolute', 265 top: 1 + ~(o.scale * o.width / 2) + 'px',
266 transform: o.hwaccel ? 'translate3d(0,0,0)' : '', 267 opacity: o.opacity, 268 animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1 / o.speed + 's linear infinite' 269 }); 270 271 if (o.shadow) ins(seg, css(fill('#000', '0 0 4px #000'), {top: '2px'})); 272 ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)'))); 273 } 274 return el; 275 }, 276 277 /** 278 * Internal method that adjusts the opacity of a single line. 279 * Will be overwritten in VML fallback mode below. 280 */ 281 opacity: function(el, i, val) { 282 if (i < el.childNodes.length) el.childNodes[i].style.opacity = val; 283 } 284 285 }); 286 287 288 function initVML() { 289 290 /* Utility function to create a VML tag */ 291 function vml(tag, attr) { 292 return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr); 293 } 294 295 // No CSS transforms but VML support, add a CSS rule for VML elements: 296 sheet.addRule('.spin-vml', 'behavior:url(#default#VML)'); 297 298 Spinner.prototype.lines = function(el, o) { 299 var r = o.scale * (o.length + o.width); 300 var s = o.scale * 2 * r; 301 302 function grp() { 303 return css( 304 vml('group', { 305 coordsize: s + ' ' + s, 306 coordorigin: -r + ' ' + -r 307 }), 308 { width: s, height: s } 309 ); 310 } 311 312 var margin = -(o.width + o.length) * o.scale * 2 + 'px'; 313 var g = css(grp(), {position: 'absolute', top: margin, left: margin}); 314 var i; 315 316 function seg(i, dx, filter) { 317 ins( 318 g, 319 ins( 320 css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}), 321 ins( 322 css( 323 vml('roundrect', {arcsize: o.corners}), 324 { 325 width: r, 326 height: o.scale * o.width, 327 left: o.scale * o.radius, 328 top: -o.scale * o.width >> 1, 329 filter: filter 330 } 331 ), 332 vml('fill', {color: getColor(o.color, i), opacity: o.opacity}), 333 vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change 334 ) 335 ) 336 ); 337 } 338 339 if (o.shadow) 340 for (i = 1; i <= o.lines; i++) { 341 seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)'); 342 } 343 344 for (i = 1; i <= o.lines; i++) seg(i); 345 return ins(el, g); 346 }; 347 348 Spinner.prototype.opacity = function(el, i, val, o) { 349 var c = el.firstChild; 350 o = o.shadow && o.lines || 0; 351 if (c && i + o < c.childNodes.length) { 352 c = c.childNodes[i + o]; c = c && c.firstChild; c = c && c.firstChild; 353 if (c) c.opacity = val; 354 } 355 }; 356 } 357 358 if (typeof document !== 'undefined') { 359 sheet = (function() { 360 var el = createEl('style', {type : 'text/css'}); 361 ins(document.getElementsByTagName('head')[0], el); 362 return el.sheet || el.styleSheet; 363 }()); 364 365 var probe = css(createEl('group'), {behavior: 'url(#default#VML)'}); 366 367 if (!vendor(probe, 'transform') && probe.adj) initVML(); 368 else useCssAnimations = vendor(probe, 'animation'); 369 } 370 371 return Spinner; 372 373})); 374 375// Ensure Spinner is available globally 376if (typeof Spinner !== 'undefined') { 377 window.Spinner = Spinner; 378}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.