1/** 2 * Copyright (c) 2011-2014 Felix Gnass 3 * Licensed under the MIT license 4 */ 5(function(root, factory) { 6 7 // CommonJS 8 if (typeof exports == 'object') { 9 module.exports = factory(); 10 } 11 // AMD module 12 else if (typeof define == 'function' && define.amd) { 13 define(factory); 14 } 15 // Browser global 16 else { 17 root.Spinner = factory(); 18 } 19 20} 21(this, function() { 22 "use strict"; 23 24 var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */ 25 , animations = {} /* Animation rules keyed by their name */ 26 , useCssAnimations /* Whether to use CSS animations or setTimeout */ 27 28 /** 29 * Utility function to create elements. If no tag name is given, 30 * a DIV is created. Optionally properties can be passed. 31 */ 32 function createEl(tag, prop) { 33 var el = document.createElement(tag || 'div') 34 , n 35 36 for(n in prop) el[n] = prop[n] 37 return el 38 } 39 40 /** 41 * Appends children and returns the parent. 42 */ 43 function ins(parent /* child1, child2, ...*/) { 44 for (var i=1, n=arguments.length; i<n; i++) 45 parent.appendChild(arguments[i]) 46 47 return parent 48 } 49 50 /** 51 * Insert a new stylesheet to hold the @keyframe or VML rules. 52 */ 53 var sheet = (function() { 54 var el = createEl('style', {type : 'text/css'}) 55 ins(document.getElementsByTagName('head')[0], el) 56 return el.sheet || el.styleSheet 57 }()) 58 59 /** 60 * Creates an opacity keyframe animation rule and returns its name. 61 * Since most mobile Webkits have timing issues with animation-delay, 62 * we create separate rules for each line/segment. 63 */ 64 function addAnimation(alpha, trail, i, lines) { 65 var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-') 66 , start = 0.01 + i/lines * 100 67 , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha) 68 , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase() 69 , pre = prefix && '-' + prefix + '-' || '' 70 71 if (!animations[name]) { 72 sheet.insertRule( 73 '@' + pre + 'keyframes ' + name + '{' + 74 '0%{opacity:' + z + '}' + 75 start + '%{opacity:' + alpha + '}' + 76 (start+0.01) + '%{opacity:1}' + 77 (start+trail) % 100 + '%{opacity:' + alpha + '}' + 78 '100%{opacity:' + z + '}' + 79 '}', sheet.cssRules.length) 80 81 animations[name] = 1 82 } 83 84 return name 85 } 86 87 /** 88 * Tries various vendor prefixes and returns the first supported property. 89 */ 90 function vendor(el, prop) { 91 var s = el.style 92 , pp 93 , i 94 95 prop = prop.charAt(0).toUpperCase() + prop.slice(1) 96 for(i=0; i<prefixes.length; i++) { 97 pp = prefixes[i]+prop 98 if(s[pp] !== undefined) return pp 99 } 100 if(s[prop] !== undefined) return prop 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 110 return el 111 } 112 113 /** 114 * Fills in default values. 115 */ 116 function merge(obj) { 117 for (var i=1; i < arguments.length; i++) { 118 var def = arguments[i] 119 for (var n in def) 120 if (obj[n] === undefined) obj[n] = def[n] 121 } 122 return obj 123 } 124 125 /** 126 * Returns the absolute page-offset of the given element. 127 */ 128 function pos(el) { 129 var o = { x:el.offsetLeft, y:el.offsetTop } 130 while((el = el.offsetParent)) 131 o.x+=el.offsetLeft, o.y+=el.offsetTop 132 133 return o 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 rotate: 0, // Rotation offset 151 corners: 1, // Roundness (0..1) 152 color: '#000', // #rgb or #rrggbb 153 direction: 1, // 1: clockwise, -1: counterclockwise 154 speed: 1, // Rounds per second 155 trail: 100, // Afterglow percentage 156 opacity: 1/4, // Opacity of the lines 157 fps: 20, // Frames per second when using setTimeout() 158 zIndex: 2e9, // Use a high z-index by default 159 className: 'spinner', // CSS class to assign to the element 160 top: '50%', // center vertically 161 left: '50%', // center horizontally 162 position: 'absolute' // element position 163 } 164 165 /** The constructor */ 166 function Spinner(o) { 167 this.opts = merge(o || {}, Spinner.defaults, defaults) 168 } 169 170 // Global defaults that override the built-ins: 171 Spinner.defaults = {} 172
173 merge(Spinner.prototype, { 174 175 /** 176 * Adds the spinner to the given target element. If this instance is already 177 * spinning, it is automatically removed from its previous target b calling 178 * stop() internally. 179 */ 180 spin: function(target) { 181 this.stop() 182 183 var self = this 184 , o = self.opts 185 , el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex}) 186 , mid = o.radius+o.length+o.width 187 188 css(el, { 189 left: o.left, 190 top: o.top 191 }) 192 193 if (target) { 194 target.insertBefore(el, target.firstChild||null) 195 } 196 197 el.setAttribute('role', 'progressbar') 198 self.lines(el, self.opts) 199 200 if (!useCssAnimations) { 201 // No CSS animation support, use setTimeout() instead 202 var i = 0 203 , start = (o.lines - 1) * (1 - o.direction) / 2 204 , alpha 205 , fps = o.fps 206 , f = fps/o.speed 207 , ostep = (1-o.opacity) / (f*o.trail / 100) 208 , astep = f/o.lines 209 210 ;(function anim() { 211 i++; 212 for (var j = 0; j < o.lines; j++) { 213 alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity) 214 215 self.opacity(el, j * o.direction + start, alpha, o) 216 } 217 self.timeout = self.el && setTimeout(anim, ~~(1000/fps)) 218 })() 219 } 220 return self 221 }, 222 223 /** 224 * Stops and removes the Spinner. 225 */ 226 stop: function() { 227 var el = this.el 228 if (el) { 229 clearTimeout(this.timeout) 230 if (el.parentNode) el.parentNode.removeChild(el) 231 this.el = undefined 232 } 233 return this 234 }, 235 236 /** 237 * Internal method that draws the individual lines. Will be overwritten 238 * in VML fallback mode below. 239 */ 240 lines: function(el, o) { 241 var i = 0 242 , start = (o.lines - 1) * (1 - o.direction) / 2 243 , seg 244 245 function fill(color, shadow) { 246 return css(createEl(), { 247 position: 'absolute', 248 width: (o.length+o.width) + 'px', 249 height: o.width + 'px', 250 background: color, 251 boxShadow: shadow, 252 transformOrigin: 'left', 253 transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)', 254 borderRadius: (o.corners * o.width>>1) + 'px' 255 }) 256 } 257 258 for (; i < o.lines; i++) { 259 seg = css(createEl(), { 260 position: 'absolute', 261 top: 1+~(o.width/2) + 'px', 262 transform: o.hwaccel ? 'translate3d(0,0,0)' : '', 263 opacity: o.opacity, 264 animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite' 265 }) 266 267 if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'})) 268 ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)'))) 269 } 270 return el 271 }, 272 273 /** 274 * Internal method that adjusts the opacity of a single line. 275 * Will be overwritten in VML fallback mode below. 276 */ 277 opacity: function(el, i, val) { 278 if (i < el.childNodes.length) el.childNodes[i].style.opacity = val 279 } 280 281 }) 282 283 284 function initVML() { 285 286 /* Utility function to create a VML tag */ 287 function vml(tag, attr) { 288 return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr) 289 } 290 291 // No CSS transforms but VML support, add a CSS rule for VML elements: 292 sheet.addRule('.spin-vml', 'behavior:url(#default#VML)') 293 294 Spinner.prototype.lines = function(el, o) { 295 var r = o.length+o.width 296 , s = 2*r 297 298 function grp() { 299 return css( 300 vml('group', { 301 coordsize: s + ' ' + s, 302 coordorigin: -r + ' ' + -r 303 }), 304 { width: s, height: s } 305 ) 306 } 307 308 var margin = -(o.width+o.length)*2 + 'px' 309 , g = css(grp(), {position: 'absolute', top: margin, left
309: margin}) 310 , i 311 312 function seg(i, dx, filter) { 313 ins(g, 314 ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}), 315 ins(css(vml('roundrect', {arcsize: o.corners}), { 316 width: r, 317 height: o.width, 318 left: o.radius, 319 top: -o.width>>1, 320 filter: filter 321 }), 322 vml('fill', {color: getColor(o.color, i), opacity: o.opacity}), 323 vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change 324 ) 325 ) 326 ) 327 } 328 329 if (o.shadow) 330 for (i = 1; i <= o.lines; i++) 331 seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)') 332 333 for (i = 1; i <= o.lines; i++) seg(i) 334 return ins(el, g) 335 } 336 337 Spinner.prototype.opacity = function(el, i, val, o) { 338 var c = el.firstChild 339 o = o.shadow && o.lines || 0 340 if (c && i+o < c.childNodes.length) { 341 c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild 342 if (c) c.opacity = val 343 } 344 } 345 } 346 347 var probe = css(createEl('group'), {behavior: 'url(#default#VML)'}) 348 349 if (!vendor(probe, 'transform') && probe.adj) initVML() 350 else useCssAnimations = vendor(probe, 'animation') 351 352 return Spinner 353 354})); 355 356 357/*! 358 * Ladda 359 * http://lab.hakim.se/ladda 360 * MIT licensed 361 * 362 * Copyright (C) 2015 Hakim El Hattab, http://hakim.se 363 */ 364/* jshint node:true, browser:true */ 365(function( root, factory ) { 366 367 // CommonJS 368 if( typeof exports === 'object' ) { 369 module.exports = factory(require('spin.js')); 370 } 371 // AMD module 372 else if( typeof define === 'function' && define.amd ) { 373 define( [ 'spin' ], factory ); 374 } 375 // Browser global 376 else { 377 root.Ladda = factory( root.Spinner ); 378 } 379 380} 381(this, function( Spinner ) { 382 'use strict'; 383 384 // All currently instantiated instances of Ladda 385 var ALL_INSTANCES = []; 386 387 /** 388 * Creates a new instance of Ladda which wraps the 389 * target button element. 390 * 391 * @return An API object that can be used to control 392 * the loading animation state. 393 */ 394 function create( button ) { 395 396 if( typeof button === 'undefined' ) { 397 console.warn( "Ladda button target must be defined." ); 398 return; 399 } 400 401 // The button must have the class "ladda-button" 402 if( !/ladda-button/i.test( button.className ) ) { 403 button.className += ' ladda-button'; 404 } 405 406 // Style is required, default to "expand-right" 407 if( !button.hasAttribute( 'data-style' ) ) { 408 button.setAttribute( 'data-style', 'expand-right' ); 409 } 410 411 // The text contents must be wrapped in a ladda-label 412 // element, create one if it doesn't already exist 413 if( !button.querySelector( '.ladda-label' ) ) { 414 var laddaLabel = document.createElement( 'span' ); 415 laddaLabel.className = 'ladda-label'; 416 wrapContent( button, laddaLabel ); 417 } 418 419 // The spinner component 420 var spinner, 421 spinnerWrapper = button.querySelector( '.ladda-spinner' ); 422 423 // Wrapper element for the spinner 424 if( !spinnerWrapper ) { 425 spinnerWrapper = document.createElement( 'span' ); 426 spinnerWrapper.className = 'ladda-spinner'; 427 } 428 429 button.appendChild( spinnerWrapper ); 430 431 // Timer used to delay starting/stopping 432 var timer; 433 434 var instance = { 435 436 /** 437 * Enter the loading state. 438 */ 439 start: function() { 440 441 // Create the spinner if it doesn't already exist 442 if( !spinner ) spinner = createSpinner( button ); 443 444 button.setAttribute( 'disabled', '' ); 445 button.setAttribute( 'data-loading', '' ); 446 447 clearTimeout( timer ); 448 spinner.spin( spinnerWrapper ); 449 450 this.setProgress( 0 ); 451 452 return this; // chain 453 454 }, 455 456 /** 457 * Enter the loading state, after a delay. 458 */ 459 startAfter: function( delay ) { 460 461 clearTimeout( timer ); 462 timer = setTimeout( function() { instance.start(); }, delay ); 463 464 return this; // chain 465 466 }, 467 468 /** 469 * Exit the loading state. 470 */ 471 stop: function() { 472 473 button.removeAttribute( 'disabled' ); 474 button.removeAttribute( 'data-loading' ); 475 476 // Kill the animation after a delay to make sure it 477 // runs for the duration of the button transition 478 clearTimeout( timer ); 479 480 if( spinner ) { 481 timer = setTimeout( function() { spinner.stop(); }, 1000 ); 482 } 483 484 return this; // chain 485 486 }, 487 488 /** 489 * Toggle the loading state on/off. 490 */ 491 toggle: function() { 492 493 if( this.isLoading() ) { 494 this.stop(); 495 } 496 else { 497 this.start(); 498 } 499 500 return this; // chain 501 502 }, 503 504 /** 505 * Sets the width of the visual progress bar inside of
506 * this Ladda button 507 * 508 * @param {Number} progress in the range of 0-1 509 */ 510 setProgress: function( progress ) { 511 512 // Cap it 513 progress = Math.max( Math.min( progress, 1 ), 0 ); 514 515 var progressElement = button.querySelector( '.ladda-progress' ); 516 517 // Remove the progress bar if we're at 0 progress 518 if( progress === 0 && progressElement && progressElement.parentNode ) { 519 progressElement.parentNode.removeChild( progressElement ); 520 } 521 else { 522 if( !progressElement ) { 523 progressElement = document.createElement( 'div' ); 524 progressElement.className = 'ladda-progress'; 525 button.appendChild( progressElement ); 526 } 527 528 progressElement.style.width = ( ( progress || 0 ) * button.offsetWidth ) + 'px'; 529 } 530 531 }, 532 533 enable: function() { 534 535 this.stop(); 536 537 return this; // chain 538 539 }, 540 541 disable: function () { 542 543 this.stop(); 544 button.setAttribute( 'disabled', '' ); 545 546 return this; // chain 547 548 }, 549 550 isLoading: function() { 551 552 return button.hasAttribute( 'data-loading' ); 553 554 }, 555 556 remove: function() { 557 558 clearTimeout( timer ); 559 560 button.removeAttribute( 'disabled', '' ); 561 button.removeAttribute( 'data-loading', '' ); 562 563 if( spinner ) { 564 spinner.stop(); 565 spinner = null; 566 } 567 568 for( var i = 0, len = ALL_INSTANCES.length; i < len; i++ ) { 569 if( instance === ALL_INSTANCES[i] ) { 570 ALL_INSTANCES.splice( i, 1 ); 571 break; 572 } 573 } 574 575 } 576 577 }; 578 579 ALL_INSTANCES.push( instance ); 580 581 return instance; 582 583 } 584 585 /** 586 * Get the first ancestor node from an element, having a 587 * certain type. 588 * 589 * @param elem An HTML element 590 * @param type an HTML tag type (uppercased) 591 * 592 * @return An HTML element 593 */ 594 function getAncestorOfTagType( elem, type ) { 595 596 while ( elem.parentNode && elem.tagName !== type ) { 597 elem = elem.parentNode; 598 } 599 600 return ( type === elem.tagName ) ? elem : undefined; 601 602 } 603 604 /** 605 * Returns a list of all inputs in the given form that 606 * have their `required` attribute set. 607 * 608 * @param form The from HTML element to look in 609 * 610 * @return A list of elements 611 */ 612 function getRequiredFields( form ) { 613 614 var requirables = [ 'input', 'textarea', 'select' ]; 615 var inputs = []; 616 617 for( var i = 0; i < requirables.length; i++ ) { 618 var candidates = form.getElementsByTagName( requirables[i] ); 619 for( var j = 0; j < candidates.length; j++ ) { 620 if ( candidates[j].hasAttribute( 'required' ) ) { 621 inputs.push( candidates[j] ); 622 } 623 } 624 } 625 626 return inputs; 627 628 } 629 630 631 /** 632 * Binds the target buttons to automatically enter the 633 * loading state when clicked. 634 * 635 * @param target Either an HTML element or a CSS selector. 636 * @param options 637 * - timeout Number of milliseconds to wait before 638 * automatically cancelling the animation. 639 */ 640 function bind( target, options ) { 641 642 options = options || {}; 643 644 var targets = []; 645 646 if( typeof target === 'string' ) { 647 targets = toArray( document.querySelectorAll( target ) ); 648 } 649 else if( typeof target === 'object' && typeof target.nodeName === 'string' ) { 650 targets = [ target ]; 651 } 652 653 for( var i = 0, len = targets.length; i < len; i++ ) { 654 655 (function() { 656 var element = targets[i]; 657 658 // Make sure we're working with a DOM element 659 if( typeof element.addEventListener === 'function' ) { 660 var instance = create( element ); 661 var timeout = -1; 662 663 element.addEventListener( 'click', function( event ) { 664 665 // If the button belongs to a form, make sure all the 666 // fields in that form are filled out 667 var valid = true; 668 var form = getAncestorOfTagType( element, 'FORM' ); 669 670 if( typeof form !== 'undefined' ) { 671 var requireds = getRequiredFields( form ); 672 for( var i = 0; i < requireds.length; i++ ) { 673 674 // Alternatively to this trim() check, 675 // we could have use .checkValidity() or .validity.valid 676 if( requireds[i].value.replace( /^\s+|\s+$/g, '' ) === '' ) { 677 valid = false; 678 } 679 680 // Radiobuttons and Checkboxes need to be checked for the "checked" attribute 681 if( (requireds[i].type === 'checkbox' || requireds[i].type === 'radio' ) && !requireds[i].checked ) { 682 valid = false; 683 } 684 685 // Email field validation, otherwise button starts spinning although field is not complete 686 if( requireds[i].type === 'email' ) { 687 valid = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test( requireds[i].value ); 688 } 689 690 } 691 } 692 693 if( valid ) { 694 // This is asynchronous to avoid an issue where setting 695 // the disabled attribute on the button prevents forms 696 // from submitting 697 instance.startAfter( 1 ); 698 699 // Set a loading timeout if one is specified 700 if( typeof options.timeout === 'number' ) { 701 clearTimeout( timeout ); 702 timeout = setTimeout( instance.stop, options.timeout ); 703 } 704 705 // Invoke callbacks 706 if( typeof options.callback === 'function' ) { 707 options.callback.apply( null, [ instance ] ); 708 } 709 } 710 711 }, false ); 712 } 713 })(); 714 715 } 716 717 } 718 719 /** 720 * Stops ALL current loading animations. 721 */ 722 function stopAll() { 723 724 for( var i = 0, len = ALL_INSTANCES.length; i < len; i++ ) { 725 ALL_INSTANCES[i].stop(); 726 } 727 728 } 729 730 function createSpinner( button ) { 731 732 var height = button.offsetHeight, 733 spinnerColor, 734 spinnerLines; 735 736 if( height === 0 ) { 737 // We may have an element that is not visible so 738 // we attempt to get the height in a different way 739 height = parseFloat( window.getComputedStyle( button ).height ); 740 } 741 742 // If the button is tall we can afford some padding 743 if( height > 32 ) { 744 height *= 0.8; 745 } 746 747 // Prefer an explicit height if one is defined 748 if( button.hasAttribute( 'data-spinner-size' ) ) { 749 height = parseInt( button.getAttribute( 'data-spinner-size' ), 10 ); 750 } 751 752 // Allow buttons to specify the color of the spinner element 753 if( button.hasAttribute( 'data-spinner-color' ) ) { 754 spinnerColor = button.getAttribute( 'data-spinner-color' ); 755 } 756 757 // Allow buttons to specify the number of lines of the spinner 758 if( button.hasAttribute( 'data-spinner-lines' ) ) { 759 spinnerLines = parseInt( button.getAttribute( 'data-spinner-lines' ), 10 ); 760 } 761 762 var radius = height * 0.2,
763 length = radius * 0.6, 764 width = radius < 7 ? 2 : 3; 765 766 return new Spinner( { 767 color: spinnerColor || '#fff', 768 lines: spinnerLines || 12, 769 radius: radius, 770 length: length, 771 width: width, 772 zIndex: 'auto', 773 top: 'auto', 774 left: 'auto', 775 className: '' 776 } ); 777 778 } 779 780 function toArray( nodes ) { 781 782 var a = []; 783 784 for ( var i = 0; i < nodes.length; i++ ) { 785 a.push( nodes[ i ] ); 786 } 787 788 return a; 789 790 } 791 792 function wrapContent( node, wrapper ) { 793 794 var r = document.createRange(); 795 r.selectNodeContents( node ); 796 r.surroundContents( wrapper ); 797 node.appendChild( wrapper ); 798 799 } 800 801 // Public API 802 return { 803 804 bind: bind, 805 create: create, 806 stopAll: stopAll 807 808 }; 809 810}));
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.