jquery.spin.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*!
  2. * ------------------------------
  3. * spin.js 1.2.7
  4. * ==============================
  5. * http://fgnass.github.com/spin.js/
  6. *
  7. * Copyright by Felix Gnass, 2011
  8. * Released under the MIT license.
  9. *
  10. * ------------------------------
  11. * jQuery spin Plugin
  12. * ==============================
  13. * https://gist.github.com/1290439
  14. *
  15. * Copyright by its-florida, 2012
  16. * Released under the MIT license.
  17. *
  18. * https://gist.github.com/4132376
  19. *
  20. * Modified by tsgautier, 2012
  21. * Released under the MIT license.
  22. *
  23. * ------------------------------
  24. * spin.js class and jQuery spin Plugin
  25. * ==============================
  26. *
  27. * Merged by loostro, 2013
  28. * Released under the MIT license.
  29. */
  30. //fgnass.github.com/spin.js#v1.2.7
  31. !function(window, document, undefined) {
  32. /**
  33. * Copyright (c) 2011 Felix Gnass [fgnass at neteye dot de]
  34. * Licensed under the MIT license
  35. */
  36. var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
  37. , animations = {} /* Animation rules keyed by their name */
  38. , useCssAnimations
  39. /**
  40. * Utility function to create elements. If no tag name is given,
  41. * a DIV is created. Optionally properties can be passed.
  42. */
  43. function createEl(tag, prop) {
  44. var el = document.createElement(tag || 'div')
  45. , n
  46. for(n in prop) el[n] = prop[n]
  47. return el
  48. }
  49. /**
  50. * Appends children and returns the parent.
  51. */
  52. function ins(parent /* child1, child2, ...*/) {
  53. for (var i=1, n=arguments.length; i<n; i++)
  54. parent.appendChild(arguments[i])
  55. return parent
  56. }
  57. /**
  58. * Insert a new stylesheet to hold the @keyframe or VML rules.
  59. */
  60. var sheet = function() {
  61. var el = createEl('style', {type : 'text/css'})
  62. ins(document.getElementsByTagName('head')[0], el)
  63. return el.sheet || el.styleSheet
  64. }()
  65. /**
  66. * Creates an opacity keyframe animation rule and returns its name.
  67. * Since most mobile Webkits have timing issues with animation-delay,
  68. * we create separate rules for each line/segment.
  69. */
  70. function addAnimation(alpha, trail, i, lines) {
  71. var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-')
  72. , start = 0.01 + i/lines*100
  73. , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
  74. , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
  75. , pre = prefix && '-'+prefix+'-' || ''
  76. if (!animations[name]) {
  77. sheet.insertRule(
  78. '@' + pre + 'keyframes ' + name + '{' +
  79. '0%{opacity:' + z + '}' +
  80. start + '%{opacity:' + alpha + '}' +
  81. (start+0.01) + '%{opacity:1}' +
  82. (start+trail) % 100 + '%{opacity:' + alpha + '}' +
  83. '100%{opacity:' + z + '}' +
  84. '}', sheet.cssRules.length)
  85. animations[name] = 1
  86. }
  87. return name
  88. }
  89. /**
  90. * Tries various vendor prefixes and returns the first supported property.
  91. **/
  92. function vendor(el, prop) {
  93. var s = el.style
  94. , pp
  95. , i
  96. if(s[prop] !== undefined) return prop
  97. prop = prop.charAt(0).toUpperCase() + prop.slice(1)
  98. for(i=0; i<prefixes.length; i++) {
  99. pp = prefixes[i]+prop
  100. if(s[pp] !== undefined) return pp
  101. }
  102. }
  103. /**
  104. * Sets multiple style properties at once.
  105. */
  106. function css(el, prop) {
  107. for (var n in prop)
  108. el.style[vendor(el, n)||n] = prop[n]
  109. return el
  110. }
  111. /**
  112. * Fills in default values.
  113. */
  114. function merge(obj) {
  115. for (var i=1; i < arguments.length; i++) {
  116. var def = arguments[i]
  117. for (var n in def)
  118. if (obj[n] === undefined) obj[n] = def[n]
  119. }
  120. return obj
  121. }
  122. /**
  123. * Returns the absolute page-offset of the given element.
  124. */
  125. function pos(el) {
  126. var o = { x:el.offsetLeft, y:el.offsetTop }
  127. while((el = el.offsetParent))
  128. o.x+=el.offsetLeft, o.y+=el.offsetTop
  129. return o
  130. }
  131. var defaults = {
  132. lines: 12, // The number of lines to draw
  133. length: 7, // The length of each line
  134. width: 5, // The line thickness
  135. radius: 10, // The radius of the inner circle
  136. rotate: 0, // Rotation offset
  137. corners: 1, // Roundness (0..1)
  138. color: '#000', // #rgb or #rrggbb
  139. speed: 1, // Rounds per second
  140. trail: 100, // Afterglow percentage
  141. opacity: 1/4, // Opacity of the lines
  142. fps: 20, // Frames per second when using setTimeout()
  143. zIndex: 2e9, // Use a high z-index by default
  144. className: 'spinner', // CSS class to assign to the element
  145. top: 'auto', // center vertically
  146. left: 'auto', // center horizontally
  147. position: 'relative' // element position
  148. }
  149. /** The constructor */
  150. var Spinner = function Spinner(o) {
  151. if (!this.spin) return new Spinner(o)
  152. this.opts = merge(o || {}, Spinner.defaults, defaults)
  153. }
  154. Spinner.defaults = {}
  155. merge(Spinner.prototype, {
  156. spin: function(target) {
  157. this.stop()
  158. var self = this
  159. , o = self.opts
  160. , el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex})
  161. , mid = o.radius+o.length+o.width
  162. , ep // element position
  163. , tp // target position
  164. if (target) {
  165. target.insertBefore(el, target.firstChild||null)
  166. tp = pos(target)
  167. ep = pos(el)
  168. css(el, {
  169. left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : parseInt(o.left, 10) + mid) + 'px',
  170. top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : parseInt(o.top, 10) + mid) + 'px'
  171. })
  172. }
  173. el.setAttribute('aria-role', 'progressbar')
  174. self.lines(el, self.opts)
  175. if (!useCssAnimations) {
  176. // No CSS animation support, use setTimeout() instead
  177. var i = 0
  178. , fps = o.fps
  179. , f = fps/o.speed
  180. , ostep = (1-o.opacity) / (f*o.trail / 100)
  181. , astep = f/o.lines
  182. ;(function anim() {
  183. i++;
  184. for (var s=o.lines; s; s--) {
  185. var alpha = Math.max(1-(i+s*astep)%f * ostep, o.opacity)
  186. self.opacity(el, o.lines-s, alpha, o)
  187. }
  188. self.timeout = self.el && setTimeout(anim, ~~(1000/fps))
  189. })()
  190. }
  191. return self
  192. },
  193. stop: function() {
  194. var el = this.el
  195. if (el) {
  196. clearTimeout(this.timeout)
  197. if (el.parentNode) el.parentNode.removeChild(el)
  198. this.el = undefined
  199. }
  200. return this
  201. },
  202. lines: function(el, o) {
  203. var i = 0
  204. , seg
  205. function fill(color, shadow) {
  206. return css(createEl(), {
  207. position: 'absolute',
  208. width: (o.length+o.width) + 'px',
  209. height: o.width + 'px',
  210. background: color,
  211. boxShadow: shadow,
  212. transformOrigin: 'left',
  213. transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
  214. borderRadius: (o.corners * o.width>>1) + 'px'
  215. })
  216. }
  217. for (; i < o.lines; i++) {
  218. seg = css(createEl(), {
  219. position: 'absolute',
  220. top: 1+~(o.width/2) + 'px',
  221. transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
  222. opacity: o.opacity,
  223. animation: useCssAnimations && addAnimation(o.opacity, o.trail, i, o.lines) + ' ' + 1/o.speed + 's linear infinite'
  224. })
  225. if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}))
  226. ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)')))
  227. }
  228. return el
  229. },
  230. opacity: function(el, i, val) {
  231. if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
  232. }
  233. })
  234. /////////////////////////////////////////////////////////////////////////
  235. // VML rendering for IE
  236. /////////////////////////////////////////////////////////////////////////
  237. /**
  238. * Check and init VML support
  239. */
  240. ;(function() {
  241. function vml(tag, attr) {
  242. return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
  243. }
  244. var s = css(createEl('group'), {behavior: 'url(#default#VML)'})
  245. if (!vendor(s, 'transform') && s.adj) {
  246. // VML support detected. Insert CSS rule ...
  247. sheet.addRule('.spin-vml', 'behavior:url(#default#VML)')
  248. Spinner.prototype.lines = function(el, o) {
  249. var r = o.length+o.width
  250. , s = 2*r
  251. function grp() {
  252. return css(
  253. vml('group', {
  254. coordsize: s + ' ' + s,
  255. coordorigin: -r + ' ' + -r
  256. }),
  257. { width: s, height: s }
  258. )
  259. }
  260. var margin = -(o.width+o.length)*2 + 'px'
  261. , g = css(grp(), {position: 'absolute', top: margin, left: margin})
  262. , i
  263. function seg(i, dx, filter) {
  264. ins(g,
  265. ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
  266. ins(css(vml('roundrect', {arcsize: o.corners}), {
  267. width: r,
  268. height: o.width,
  269. left: o.radius,
  270. top: -o.width>>1,
  271. filter: filter
  272. }),
  273. vml('fill', {color: o.color, opacity: o.opacity}),
  274. vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
  275. )
  276. )
  277. )
  278. }
  279. if (o.shadow)
  280. for (i = 1; i <= o.lines; i++)
  281. seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
  282. for (i = 1; i <= o.lines; i++) seg(i)
  283. return ins(el, g)
  284. }
  285. Spinner.prototype.opacity = function(el, i, val, o) {
  286. var c = el.firstChild
  287. o = o.shadow && o.lines || 0
  288. if (c && i+o < c.childNodes.length) {
  289. c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild
  290. if (c) c.opacity = val
  291. }
  292. }
  293. }
  294. else
  295. useCssAnimations = vendor(s, 'animation')
  296. })()
  297. if (typeof define == 'function' && define.amd)
  298. define(function() { return Spinner })
  299. else
  300. window.Spinner = Spinner
  301. }(window, document);
  302. /**
  303. * jQuery spin Plugin 1.2.7
  304. *
  305. * Created by its-florida
  306. * Modified by tsgautier
  307. *
  308. * Released under the MIT license.
  309. */
  310. (function($) {
  311. $.fn.spin = function(opts, color) {
  312. if (arguments.length == 1 && opts == false) {
  313. return this.each(function() {
  314. var $this = $(this),
  315. data = $this.data();
  316. if (data.spinner) {
  317. data.spinner.stop();
  318. delete data.spinner;
  319. }
  320. });
  321. }
  322. var presets = {
  323. "tiny": { lines: 8, length: 2, width: 2, radius: 3 },
  324. "small": { lines: 8, length: 4, width: 3, radius: 5 },
  325. "large": { lines: 10, length: 8, width: 4, radius: 8 }
  326. };
  327. if (Spinner) {
  328. return this.each(function() {
  329. var $this = $(this),
  330. data = $this.data();
  331. if (data.spinner) {
  332. data.spinner.stop();
  333. delete data.spinner;
  334. }
  335. if (opts !== false) {
  336. if (typeof opts === "string") {
  337. if (opts in presets) {
  338. opts = presets[opts];
  339. } else {
  340. opts = {};
  341. }
  342. if (color) {
  343. opts.color = color;
  344. }
  345. }
  346. data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
  347. }
  348. });
  349. } else {
  350. throw "Spinner class not available.";
  351. }
  352. };
  353. })(jQuery);