github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/static/semantic/src/definitions/modules/shape.js (about) 1 /*! 2 * # Semantic UI - Shape 3 * http://github.com/semantic-org/semantic-ui/ 4 * 5 * 6 * Copyright 2014 Contributors 7 * Released under the MIT license 8 * http://opensource.org/licenses/MIT 9 * 10 */ 11 12 ;(function ( $, window, document, undefined ) { 13 14 "use strict"; 15 16 $.fn.shape = function(parameters) { 17 var 18 $allModules = $(this), 19 $body = $('body'), 20 21 time = new Date().getTime(), 22 performance = [], 23 24 query = arguments[0], 25 methodInvoked = (typeof query == 'string'), 26 queryArguments = [].slice.call(arguments, 1), 27 28 requestAnimationFrame = window.requestAnimationFrame 29 || window.mozRequestAnimationFrame 30 || window.webkitRequestAnimationFrame 31 || window.msRequestAnimationFrame 32 || function(callback) { setTimeout(callback, 0); }, 33 34 returnedValue 35 ; 36 37 $allModules 38 .each(function() { 39 var 40 moduleSelector = $allModules.selector || '', 41 settings = $.extend(true, {}, $.fn.shape.settings, parameters), 42 43 // internal aliases 44 namespace = settings.namespace, 45 selector = settings.selector, 46 error = settings.error, 47 className = settings.className, 48 49 // define namespaces for modules 50 eventNamespace = '.' + namespace, 51 moduleNamespace = 'module-' + namespace, 52 53 // selector cache 54 $module = $(this), 55 $sides = $module.find(selector.sides), 56 $side = $module.find(selector.side), 57 58 // private variables 59 nextIndex = false, 60 $activeSide, 61 $nextSide, 62 63 // standard module 64 element = this, 65 instance = $module.data(moduleNamespace), 66 module 67 ; 68 69 module = { 70 71 initialize: function() { 72 module.verbose('Initializing module for', element); 73 module.set.defaultSide(); 74 module.instantiate(); 75 }, 76 77 instantiate: function() { 78 module.verbose('Storing instance of module', module); 79 instance = module; 80 $module 81 .data(moduleNamespace, instance) 82 ; 83 }, 84 85 destroy: function() { 86 module.verbose('Destroying previous module for', element); 87 $module 88 .removeData(moduleNamespace) 89 .off(eventNamespace) 90 ; 91 }, 92 93 refresh: function() { 94 module.verbose('Refreshing selector cache for', element); 95 $module = $(element); 96 $sides = $(this).find(selector.shape); 97 $side = $(this).find(selector.side); 98 }, 99 100 repaint: function() { 101 module.verbose('Forcing repaint event'); 102 var 103 shape = $sides.get(0) || document.createElement('div'), 104 fakeAssignment = shape.offsetWidth 105 ; 106 }, 107 108 animate: function(propertyObject, callback) { 109 module.verbose('Animating box with properties', propertyObject); 110 callback = callback || function(event) { 111 module.verbose('Executing animation callback'); 112 if(event !== undefined) { 113 event.stopPropagation(); 114 } 115 module.reset(); 116 module.set.active(); 117 }; 118 settings.beforeChange.call($nextSide.get()); 119 if(module.get.transitionEvent()) { 120 module.verbose('Starting CSS animation'); 121 $module 122 .addClass(className.animating) 123 ; 124 $sides 125 .css(propertyObject) 126 .one(module.get.transitionEvent(), callback) 127 ; 128 module.set.duration(settings.duration); 129 requestAnimationFrame(function() { 130 $module 131 .addClass(className.animating) 132 ; 133 $activeSide 134 .addClass(className.hidden) 135 ; 136 }); 137 } 138 else { 139 callback(); 140 } 141 }, 142 143 queue: function(method) { 144 module.debug('Queueing animation of', method); 145 $sides 146 .one(module.get.transitionEvent(), function() { 147 module.debug('Executing queued animation'); 148 setTimeout(function(){ 149 $module.shape(method); 150 }, 0); 151 }) 152 ; 153 }, 154 155 reset: function() { 156 module.verbose('Animating states reset'); 157 $module 158 .removeClass(className.animating) 159 .attr('style', '') 160 .removeAttr('style') 161 ; 162 // removeAttr style does not consistently work in safari 163 $sides 164 .attr('style', '') 165 .removeAttr('style') 166 ; 167 $side 168 .attr('style', '') 169 .removeAttr('style') 170 .removeClass(className.hidden) 171 ; 172 $nextSide 173 .removeClass(className.animating) 174 .attr('style', '') 175 .removeAttr('style') 176 ; 177 }, 178 179 is: { 180 complete: function() { 181 return ($side.filter('.' + className.active)[0] == $nextSide[0]); 182 }, 183 animating: function() { 184 return $module.hasClass(className.animating); 185 } 186 }, 187 188 set: { 189 190 defaultSide: function() { 191 $activeSide = $module.find('.' + settings.className.active); 192 $nextSide = ( $activeSide.next(selector.side).length > 0 ) 193 ? $activeSide.next(selector.side) 194 : $module.find(selector.side).first() 195 ; 196 nextIndex = false; 197 module.verbose('Active side set to', $activeSide); 198 module.verbose('Next side set to', $nextSide); 199 }, 200 201 duration: function(duration) { 202 duration = duration || settings.duration; 203 duration = (typeof duration == 'number') 204 ? duration + 'ms' 205 : duration 206 ; 207 module.verbose('Setting animation duration', duration); 208 $sides.add($side) 209 .css({ 210 '-webkit-transition-duration': duration, 211 '-moz-transition-duration': duration, 212 '-ms-transition-duration': duration, 213 '-o-transition-duration': duration, 214 'transition-duration': duration 215 }) 216 ; 217 }, 218 219 stageSize: function() { 220 var 221 $clone = $module.clone().addClass(className.loading), 222 $activeSide = $clone.find('.' + settings.className.active), 223 $nextSide = (nextIndex) 224 ? $clone.find(selector.side).eq(nextIndex) 225 : ( $activeSide.next(selector.side).length > 0 ) 226 ? $activeSide.next(selector.side) 227 : $clone.find(selector.side).first(), 228 newSize = {} 229 ; 230 $activeSide.removeClass(className.active); 231 $nextSide.addClass(className.active); 232 $clone.insertAfter($module); 233 newSize = { 234 width : $nextSide.outerWidth(), 235 height : $nextSide.outerHeight() 236 }; 237 $clone.remove(); 238 $module 239 .css(newSize) 240 ; 241 module.verbose('Resizing stage to fit new content', newSize); 242 }, 243 244 nextSide: function(selector) { 245 nextIndex = selector; 246 $nextSide = $side.filter(selector); 247 nextIndex = $side.index($nextSide); 248 if($nextSide.length === 0) { 249 module.set.defaultSide(); 250 module.error(error.side); 251 } 252 module.verbose('Next side manually set to', $nextSide); 253 }, 254 255 active: function() { 256 module.verbose('Setting new side to active', $nextSide); 257 $side 258 .removeClass(className.active) 259 ; 260 $nextSide 261 .addClass(className.active) 262 ; 263 settings.onChange.call($nextSide.get()); 264 module.set.defaultSide(); 265 } 266 }, 267 268 flip: { 269 270 up: function() { 271 if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) { 272 module.debug('Side already visible', $nextSide); 273 return; 274 } 275 if( !module.is.animating()) { 276 module.debug('Flipping up', $nextSide); 277 module.set.stageSize(); 278 module.stage.above(); 279 module.animate( module.get.transform.up() ); 280 } 281 else { 282 module.queue('flip up'); 283 } 284 }, 285 286 down: function() { 287 if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) { 288 module.debug('Side already visible', $nextSide); 289 return; 290 } 291 if( !module.is.animating()) { 292 module.debug('Flipping down', $nextSide); 293 module.set.stageSize(); 294 module.stage.below(); 295 module.animate( module.get.transform.down() ); 296 } 297 else { 298 module.queue('flip down'); 299 } 300 }, 301 302 left: function() { 303 if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) { 304 module.debug('Side already visible', $nextSide); 305 return; 306 } 307 if( !module.is.animating()) { 308 module.debug('Flipping left', $nextSide); 309 module.set.stageSize(); 310 module.stage.left(); 311 module.animate(module.get.transform.left() ); 312 } 313 else { 314 module.queue('flip left'); 315 } 316 }, 317 318 right: function() { 319 if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) { 320 module.debug('Side already visible', $nextSide); 321 return; 322 } 323 if( !module.is.animating()) { 324 module.debug('Flipping right', $nextSide); 325 module.set.stageSize(); 326 module.stage.right(); 327 module.animate(module.get.transform.right() ); 328 } 329 else { 330 module.queue('flip right'); 331 } 332 }, 333 334 over: function() { 335 if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) { 336 module.debug('Side already visible', $nextSide); 337 return; 338 } 339 if( !module.is.animating()) { 340 module.debug('Flipping over', $nextSide); 341 module.set.stageSize(); 342 module.stage.behind(); 343 module.animate(module.get.transform.over() ); 344 } 345 else { 346 module.queue('flip over'); 347 } 348 }, 349 350 back: function() { 351 if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) { 352 module.debug('Side already visible', $nextSide); 353 return; 354 } 355 if( !module.is.animating()) { 356 module.debug('Flipping back', $nextSide); 357 module.set.stageSize(); 358 module.stage.behind(); 359 module.animate(module.get.transform.back() ); 360 } 361 else { 362 module.queue('flip back'); 363 } 364 } 365 366 }, 367 368 get: { 369 370 transform: { 371 up: function() { 372 var 373 translate = { 374 y: -(($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), 375 z: -($activeSide.outerHeight() / 2) 376 } 377 ; 378 return { 379 transform: 'translateY(' + translate.y + 'px) translateZ('+ translate.z + 'px) rotateX(-90deg)' 380 }; 381 }, 382 383 down: function() { 384 var 385 translate = { 386 y: -(($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), 387 z: -($activeSide.outerHeight() / 2) 388 } 389 ; 390 return { 391 transform: 'translateY(' + translate.y + 'px) translateZ('+ translate.z + 'px) rotateX(90deg)' 392 }; 393 }, 394 395 left: function() { 396 var 397 translate = { 398 x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2), 399 z : -($activeSide.outerWidth() / 2) 400 } 401 ; 402 return { 403 transform: 'translateX(' + translate.x + 'px) translateZ(' + translate.z + 'px) rotateY(90deg)' 404 }; 405 }, 406 407 right: function() { 408 var 409 translate = { 410 x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2), 411 z : -($activeSide.outerWidth() / 2) 412 } 413 ; 414 return { 415 transform: 'translateX(' + translate.x + 'px) translateZ(' + translate.z + 'px) rotateY(-90deg)' 416 }; 417 }, 418 419 over: function() { 420 var 421 translate = { 422 x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2) 423 } 424 ; 425 return { 426 transform: 'translateX(' + translate.x + 'px) rotateY(180deg)' 427 }; 428 }, 429 430 back: function() { 431 var 432 translate = { 433 x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2) 434 } 435 ; 436 return { 437 transform: 'translateX(' + translate.x + 'px) rotateY(-180deg)' 438 }; 439 } 440 }, 441 442 transitionEvent: function() { 443 var 444 element = document.createElement('element'), 445 transitions = { 446 'transition' :'transitionend', 447 'OTransition' :'oTransitionEnd', 448 'MozTransition' :'transitionend', 449 'WebkitTransition' :'webkitTransitionEnd' 450 }, 451 transition 452 ; 453 for(transition in transitions){ 454 if( element.style[transition] !== undefined ){ 455 return transitions[transition]; 456 } 457 } 458 }, 459 460 nextSide: function() { 461 return ( $activeSide.next(selector.side).length > 0 ) 462 ? $activeSide.next(selector.side) 463 : $module.find(selector.side).first() 464 ; 465 } 466 467 }, 468 469 stage: { 470 471 above: function() { 472 var 473 box = { 474 origin : (($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), 475 depth : { 476 active : ($nextSide.outerHeight() / 2), 477 next : ($activeSide.outerHeight() / 2) 478 } 479 } 480 ; 481 module.verbose('Setting the initial animation position as above', $nextSide, box); 482 $activeSide 483 .css({ 484 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' 485 }) 486 ; 487 $nextSide 488 .addClass(className.animating) 489 .css({ 490 'display' : 'block', 491 'top' : box.origin + 'px', 492 'transform' : 'rotateX(90deg) translateZ(' + box.depth.next + 'px)' 493 }) 494 ; 495 }, 496 497 below: function() { 498 var 499 box = { 500 origin : (($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), 501 depth : { 502 active : ($nextSide.outerHeight() / 2), 503 next : ($activeSide.outerHeight() / 2) 504 } 505 } 506 ; 507 module.verbose('Setting the initial animation position as below', $nextSide, box); 508 $activeSide 509 .css({ 510 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' 511 }) 512 ; 513 $nextSide 514 .addClass(className.animating) 515 .css({ 516 'display' : 'block', 517 'top' : box.origin + 'px', 518 'transform' : 'rotateX(-90deg) translateZ(' + box.depth.next + 'px)' 519 }) 520 ; 521 }, 522 523 left: function() { 524 var 525 box = { 526 origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2), 527 depth : { 528 active : ($nextSide.outerWidth() / 2), 529 next : ($activeSide.outerWidth() / 2) 530 } 531 } 532 ; 533 module.verbose('Setting the initial animation position as left', $nextSide, box); 534 $activeSide 535 .css({ 536 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' 537 }) 538 ; 539 $nextSide 540 .addClass(className.animating) 541 .css({ 542 'display' : 'block', 543 'left' : box.origin + 'px', 544 'transform' : 'rotateY(-90deg) translateZ(' + box.depth.next + 'px)' 545 }) 546 ; 547 }, 548 549 right: function() { 550 var 551 box = { 552 origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2), 553 depth : { 554 active : ($nextSide.outerWidth() / 2), 555 next : ($activeSide.outerWidth() / 2) 556 } 557 } 558 ; 559 module.verbose('Setting the initial animation position as left', $nextSide, box); 560 $activeSide 561 .css({ 562 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' 563 }) 564 ; 565 $nextSide 566 .addClass(className.animating) 567 .css({ 568 'display' : 'block', 569 'left' : box.origin + 'px', 570 'transform' : 'rotateY(90deg) translateZ(' + box.depth.next + 'px)' 571 }) 572 ; 573 }, 574 575 behind: function() { 576 var 577 box = { 578 origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2), 579 depth : { 580 active : ($nextSide.outerWidth() / 2), 581 next : ($activeSide.outerWidth() / 2) 582 } 583 } 584 ; 585 module.verbose('Setting the initial animation position as behind', $nextSide, box); 586 $activeSide 587 .css({ 588 'transform' : 'rotateY(0deg)' 589 }) 590 ; 591 $nextSide 592 .addClass(className.animating) 593 .css({ 594 'display' : 'block', 595 'left' : box.origin + 'px', 596 'transform' : 'rotateY(-180deg)' 597 }) 598 ; 599 } 600 }, 601 setting: function(name, value) { 602 module.debug('Changing setting', name, value); 603 if( $.isPlainObject(name) ) { 604 $.extend(true, settings, name); 605 } 606 else if(value !== undefined) { 607 settings[name] = value; 608 } 609 else { 610 return settings[name]; 611 } 612 }, 613 internal: function(name, value) { 614 if( $.isPlainObject(name) ) { 615 $.extend(true, module, name); 616 } 617 else if(value !== undefined) { 618 module[name] = value; 619 } 620 else { 621 return module[name]; 622 } 623 }, 624 debug: function() { 625 if(settings.debug) { 626 if(settings.performance) { 627 module.performance.log(arguments); 628 } 629 else { 630 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); 631 module.debug.apply(console, arguments); 632 } 633 } 634 }, 635 verbose: function() { 636 if(settings.verbose && settings.debug) { 637 if(settings.performance) { 638 module.performance.log(arguments); 639 } 640 else { 641 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); 642 module.verbose.apply(console, arguments); 643 } 644 } 645 }, 646 error: function() { 647 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); 648 module.error.apply(console, arguments); 649 }, 650 performance: { 651 log: function(message) { 652 var 653 currentTime, 654 executionTime, 655 previousTime 656 ; 657 if(settings.performance) { 658 currentTime = new Date().getTime(); 659 previousTime = time || currentTime; 660 executionTime = currentTime - previousTime; 661 time = currentTime; 662 performance.push({ 663 'Name' : message[0], 664 'Arguments' : [].slice.call(message, 1) || '', 665 'Element' : element, 666 'Execution Time' : executionTime 667 }); 668 } 669 clearTimeout(module.performance.timer); 670 module.performance.timer = setTimeout(module.performance.display, 100); 671 }, 672 display: function() { 673 var 674 title = settings.name + ':', 675 totalTime = 0 676 ; 677 time = false; 678 clearTimeout(module.performance.timer); 679 $.each(performance, function(index, data) { 680 totalTime += data['Execution Time']; 681 }); 682 title += ' ' + totalTime + 'ms'; 683 if(moduleSelector) { 684 title += ' \'' + moduleSelector + '\''; 685 } 686 if($allModules.length > 1) { 687 title += ' ' + '(' + $allModules.length + ')'; 688 } 689 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { 690 console.groupCollapsed(title); 691 if(console.table) { 692 console.table(performance); 693 } 694 else { 695 $.each(performance, function(index, data) { 696 console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); 697 }); 698 } 699 console.groupEnd(); 700 } 701 performance = []; 702 } 703 }, 704 invoke: function(query, passedArguments, context) { 705 var 706 object = instance, 707 maxDepth, 708 found, 709 response 710 ; 711 passedArguments = passedArguments || queryArguments; 712 context = element || context; 713 if(typeof query == 'string' && object !== undefined) { 714 query = query.split(/[\. ]/); 715 maxDepth = query.length - 1; 716 $.each(query, function(depth, value) { 717 var camelCaseValue = (depth != maxDepth) 718 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) 719 : query 720 ; 721 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { 722 object = object[camelCaseValue]; 723 } 724 else if( object[camelCaseValue] !== undefined ) { 725 found = object[camelCaseValue]; 726 return false; 727 } 728 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { 729 object = object[value]; 730 } 731 else if( object[value] !== undefined ) { 732 found = object[value]; 733 return false; 734 } 735 else { 736 return false; 737 } 738 }); 739 } 740 if ( $.isFunction( found ) ) { 741 response = found.apply(context, passedArguments); 742 } 743 else if(found !== undefined) { 744 response = found; 745 } 746 if($.isArray(returnedValue)) { 747 returnedValue.push(response); 748 } 749 else if(returnedValue !== undefined) { 750 returnedValue = [returnedValue, response]; 751 } 752 else if(response !== undefined) { 753 returnedValue = response; 754 } 755 return found; 756 } 757 }; 758 759 if(methodInvoked) { 760 if(instance === undefined) { 761 module.initialize(); 762 } 763 module.invoke(query); 764 } 765 else { 766 if(instance !== undefined) { 767 instance.invoke('destroy'); 768 } 769 module.initialize(); 770 } 771 }) 772 ; 773 774 return (returnedValue !== undefined) 775 ? returnedValue 776 : this 777 ; 778 }; 779 780 $.fn.shape.settings = { 781 782 // module info 783 name : 'Shape', 784 785 // debug content outputted to console 786 debug : false, 787 788 // verbose debug output 789 verbose : true, 790 791 // performance data output 792 performance: true, 793 794 // event namespace 795 namespace : 'shape', 796 797 // callback occurs on side change 798 beforeChange : function() {}, 799 onChange : function() {}, 800 801 // allow animation to same side 802 allowRepeats: false, 803 804 // animation duration 805 duration : 700, 806 807 // possible errors 808 error: { 809 side : 'You tried to switch to a side that does not exist.', 810 method : 'The method you called is not defined' 811 }, 812 813 // classnames used 814 className : { 815 animating : 'animating', 816 hidden : 'hidden', 817 loading : 'loading', 818 active : 'active' 819 }, 820 821 // selectors used 822 selector : { 823 sides : '.sides', 824 side : '.side' 825 } 826 827 }; 828 829 830 })( jQuery, window , document );