1/* 2* MIXITUP - A CSS3 and JQuery Filter & Sort Plugin 3* Version: 1.5.4 4* License: Creative Commons Attribution-NoDerivs 3.0 Unported - CC BY-ND 3.0 5* http://creativecommons.org/licenses/by-nd/3.0/ 6* This software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder. 7* Author: Patrick Kunka 8* Copyright 2012-2013 Patrick Kunka, Barrel LLC, All Rights Reserved 9* 10* http://mixitup.io 11*/ 12 13(function($){ 14 15 // DECLARE METHODS 16 17 var methods = { 18 19 // "INIT" METHOD 20 21 init: function(settings){ 22 23 return this.each(function(){ 24 25 // BUILD CONFIG OBJECT 26 27 var config = { 28 29 // PUBLIC PROPERTIES 30 31 targetSelector : '.mix', 32 filterSelector : '.filter', 33 sortSelector : '.sort', 34 buttonEvent: 'click', 35 effects : ['fade', 'scale'], 36 listEffects : null, 37 easing : 'smooth', 38 layoutMode: 'grid', 39 targetDisplayGrid : 'inline-block', 40 targetDisplayList: 'block', 41 listClass : '', 42 gridClass : '', 43 transitionSpeed : 600, 44 showOnLoad : 'all', 45 sortOnLoad : false, 46 multiFilter : false, 47 filterLogic : 'or', 48 resizeContainer : true, 49 minHeight : 0, 50 failClass : 'fail', 51 perspectiveDistance : '3000', 52 perspectiveOrigin : '50% 50%', 53 animateGridList : true, 54 onMixLoad: null, 55 onMixStart : null, 56 onMixEnd : null, 57 58 // MISC 59 60 container : null, 61 origOrder : [], 62 startOrder : [], 63 newOrder : [], 64 origSort: [], 65 checkSort: [], 66 filter : '', 67 mixing : false, 68 origDisplay : '', 69 origLayout: '', 70 origHeight : 0, 71 newHeight : 0, 72 isTouch : false, 73 resetDelay : 0, 74 failsafe : null, 75 76 // CSS 77 78 prefix : '', 79 easingFallback : 'ease-in-out', 80 transition : {}, 81 perspective : {}, 82 clean : {}, 83 fade : '1', 84 scale : '', 85 rotateX : '', 86 rotateY : '', 87 rotateZ : '', 88 blur : '', 89 grayscale : '' 90 }; 91 92 if(settings){ 93 $.extend(config, settings); 94 }; 95 96 // ADD CONFIG OBJECT TO CONTAINER OBJECT PER INSTANTIATION 97 98 this.config = config; 99 100 // DETECT TOUCH 101 102 $.support.touch = 'ontouchend' in document; 103 104 if ($.support.touch) { 105 config.isTouch = true; 106 config.resetDelay = 350; 107 }; 108 109 // LOCALIZE CONTAINER 110 111 config.container = $(this); 112 var $cont = config.container; 113 114 // GET VENDOR PREFIX 115 116 config.prefix = prefix($cont[0]); 117 config.prefix = config.prefix ? '-'+config.prefix.toLowerCase()+'-' : ''; 118 119 // CACHE 'DEFAULT' SORTING ORDER 120 121 $cont.find(config.targetSelector).each(function(){ 122 config.origOrder.push($(this)); 123 }); 124 125 // PERFORM SORT ON LOAD 126 127 if(config.sortOnLoad){ 128 var sortby, order; 129 if($.isArray(config.sortOnLoad)){ 130 sortby = config.sortOnLoad[0], order = config.sortOnLoad[1]; 131 $(config.sortSelector+'[data-sort='+config.sortOnLoad[0]+'][data-order='+config.sortOnLoad[1]+']').addClass('active'); 132 } else { 133 $(config.sortSelector+'[data-sort='+config.sortOnLoad+']').addClass('active'); 134 sortby = config.sortOnLoad, config.sortOnLoad = 'desc'; 135 }; 136 sort(sortby, order, $cont, config); 137 }; 138 139 // BUILD TRANSITION AND PERSPECTIVE OBJECTS 140 141 for(var i = 0; i<2; i++){ 142 var a = i==0 ? a = config.prefix : ''; 143 config.transition[a+'transition'] = 'all '+config.transitionSpeed+'ms ease-in-out'; 144 config.perspective[a+'perspective'] = config.perspectiveDistance+'px'; 145 config.perspective[a+'perspective-origin'] = config.perspectiveOrigin; 146 }; 147 148 // BUILD TRANSITION CLEANER 149 150 for(var i = 0; i<2; i++){ 151 var a = i==0 ? a = config.prefix : ''; 152 config.clean[a+'transition'] = 'none'; 153 }; 154 155 // CHOOSE GRID OR LIST 156 157 if(config.layoutMode == 'list'){ 158 $cont.addClass(config.listClass); 159 config.origDisplay = config.targetDisplayList; 160 } else { 161 $cont.addClass(config.gridClass); 162 config.origDisplay = config.targetDisplayGrid; 163 }; 164 config.origLayout = config.layoutMode; 165 166 // PARSE 'SHOWONLOAD' 167 168 var showOnLoadArray = config.showOnLoad.split(' '); 169 170 // GIVE ACTIVE FILTER ACTIVE CLASS 171 172 $.each(showOnLoadArray, function(){ 173 $(config.filterSelector+'[data-filter="'+this+'"]').addClass('active'); 174 }); 175 176 // RENAME "ALL" CATEGORY TO "MIX_ALL" 177 178 $cont.find(config.targetSelector).addClass('mix_all'); 179 if(showOnLoadArray[0] == 'all'){ 180 showOnLoadArray[0] = 'mix_all', 181 config.showOnLoad = 'mix_all'; 182 }; 183 184 // FADE IN 'SHOWONLOAD' 185 186 var $showOnLoad = $(); 187 $.each(showOnLoadArray, function(){ 188 $showOnLoad = $showOnLoad.add($('.'+this)) 189 }); 190 191 $showOnLoad.each(function(){ 192 var $t = $(this); 193 if(config.layoutMode == 'list'){ 194 $t.css('display',config.targetDisplayList); 195 } else { 196 $t.css('display',config.targetDisplayGrid); 197 }; 198 $t.css(config.transition); 199 }); 200 201 // WRAP FADE-IN TO PREVENT RACE CONDITION 202
203 var delay = setTimeout(function(){ 204 205 config.mixing = true; 206 207 $showOnLoad.css('opacity','1'); 208 209 // CLEAN UP 210 211 var reset = setTimeout(function(){ 212 if(config.layoutMode == 'list'){ 213 $showOnLoad.removeStyle(config.prefix+'transition, transition').css({ 214 display: config.targetDisplayList, 215 opacity: 1 216 }); 217 } else { 218 $showOnLoad.removeStyle(config.prefix+'transition, transition').css({ 219 display: config.targetDisplayGrid, 220 opacity: 1 221 }); 222 }; 223 224 // FIRE "ONMIXLOAD" CALLBACK 225 226 config.mixing = false; 227 228 if(typeof config.onMixLoad == 'function') { 229 var output = config.onMixLoad.call(this, config); 230 231 // UPDATE CONFIG IF DATA RETURNED 232 233 config = output ? output : config; 234 }; 235 236 },config.transitionSpeed); 237 },10); 238 239 // PRESET ACTIVE FILTER 240 241 config.filter = config.showOnLoad; 242 243 // BIND SORT CLICK HANDLERS 244 245 $(config.sortSelector).bind(config.buttonEvent,function(){ 246 247 if(!config.mixing){ 248 249 // PARSE SORT ARGUMENTS FROM BUTTON CLASSES 250 251 var $t = $(this), 252 sortby = $t.attr('data-sort'), 253 order = $t.attr('data-order'); 254 255 if(!$t.hasClass('active')){ 256 $(config.sortSelector).removeClass('active'); 257 $t.addClass('active'); 258 } else { 259 if(sortby != 'random')return false; 260 }; 261 262 $cont.find(config.targetSelector).each(function(){ 263 config.startOrder.push($(this)); 264 }); 265 266 goMix(config.filter,sortby,order,$cont, config); 267 268 }; 269 270 }); 271 272 // BIND FILTER CLICK HANDLERS 273 274 $(config.filterSelector).bind(config.buttonEvent,function(){ 275 276 if(!config.mixing){ 277 278 var $t = $(this); 279 280 // PARSE FILTER ARGUMENTS FROM BUTTON CLASSES 281 282 if(config.multiFilter == false){ 283 284 // SINGLE ACTIVE BUTTON 285 286 $(config.filterSelector).removeClass('active'); 287 $t.addClass('active'); 288 289 config.filter = $t.attr('data-filter'); 290 291 $(config.filterSelector+'[data-filter="'+config.filter+'"]').addClass('active'); 292 293 } else { 294 295 // MULTIPLE ACTIVE BUTTONS 296 297 var thisFilter = $t.attr('data-filter'); 298 299 if($t.hasClass('active')){ 300 $t.removeClass('active'); 301 302 // REMOVE FILTER FROM SPACE-SEPERATED STRING 303 304 var re = new RegExp('(\\s|^)'+thisFilter); 305 config.filter = config.filter.replace(re,''); 306 } else { 307 308 // ADD FILTER TO SPACE-SEPERATED STRING 309 310 $t.addClass('active'); 311 config.filter = config.filter+' '+thisFilter; 312 313 }; 314 }; 315 316 // GO MIX 317 318 goMix(config.filter, null, null, $cont, config); 319 320 }; 321 322 }); 323 324 }); 325 }, 326 327 // "TOGRID" METHOD 328 329 toGrid: function(){ 330 return this.each(function(){ 331 var config = this.config; 332 if(config.layoutMode != 'grid'){ 333 config.layoutMode = 'grid'; 334 goMix(config.filter, null, null, $(this), config); 335 }; 336 }); 337 }, 338 339 // "TOLIST" METHOD 340 341 toList: function(){ 342 return this.each(function(){ 343 var config = this.config; 344 if(config.layoutMode != 'list'){ 345 config.layoutMode = 'list'; 346 goMix(config.filter, null, null, $(this), config); 347 }; 348 }); 349 }, 350 351 // "FILTER" METHOD 352 353 filter: function(arg){ 354 return this.each(function(){ 355 var config = this.config; 356 if(!config.mixing){ 357 $(config.filterSelector).removeClass('active'); 358 $(config.filterSelector+'[data-filter="'+arg+'"]').addClass('active'); 359 goMix(arg, null, null, $(this), config); 360 }; 361 }); 362 }, 363 364 // "SORT" METHOD 365 366 sort: function(args){ 367 return this.each(function(){ 368 var config = this.config, 369 $t = $(this); 370 if(!config.mixing){ 371 $(config.sortSelector).removeClass('active'); 372 if($.isArray(args)){ 373 var sortby = args[0], order = args[1]; 374 $(config.sortSelector+'[data-sort="'+args[0]+'"][data-order="'+args[1]+'"]').addClass('active'); 375 } else { 376 $(config.sortSelector+'[data-sort="'+args+'"]').addClass('active'); 377 var sortby = args, order = 'desc'; 378 }; 379 $t.find(config.targetSelector).each(function(){ 380 config.startOrder.push($(this)); 381 }); 382 383 goMix(config.filter,sortby,order, $t, config); 384 385 }; 386 }); 387 }, 388 389 // "MULTIMIX" METHOD 390 391 multimix: function(args){ 392 return this.each(function(){ 393 var config = this.config, 394 $t = $(this); 395 multiOut = { 396 filter: config.filter, 397 sort: null, 398 order: 'desc', 399 layoutMode: config.layoutMode 400 }; 401 $.extend(multiOut, args); 402 if(!config.mixing){ 403 $(config.filterSelector).add(config.sortSelector).removeClass('active'); 404 $(config.filterSelector+'[data-filter="'+multiOut.filter+'"]').addClass('active'); 405 if(typeof multiOut.sort !== 'undefined'){ 406 $(config.sortSelector+'[data-sort="'+multiOut.sort+'"][data-order="'+multiOut.order+'"]').addClass('active'); 407 $t.find(config.targetSelector).each(function(){ 408 config.startOrder.push($(this)); 409 }); 410 }; 411 config.layoutMode = multiOut.layoutMode; 412 goMix(multiOut.filter,multiOut.sort,multiOut.order, $t, config); 413 }; 414 }); 415 }, 416 417 // "REMIX" METHOD 418 419 remix: function(arg){ 420 return this.each(function(){ 421 var config = this.config, 422 $t = $(this); 423 config.origOrder = []; 424 $t.find(config.targetSelector).each(function(){ 425 var $th = $(this); 426 $th.addClass('mix_all'); 427 config.origOrder.push($th); 428 }); 429 if(!config.mixing && typeof arg !== 'undefined'){ 430 $(config.filterSelector).removeClass('active'); 431 $(config.filterSelector+'[data-filter="'+arg+'"]').addClass('active'); 432 goMix(arg, null, null, $t, config); 433 }; 434 }); 435 } 436 }; 437 438 // DECLARE PLUGIN 439 440 $.fn.mixitup = function(method, arg){ 441 if (methods[method]) { 442 return methods[method].apply( this, Array.prototype.slice.call(arguments,1
442)); 443 } else if (typeof method === 'object' || ! method){ 444 return methods.init.apply( this, arguments ); 445 }; 446 }; 447 448 /* ==== THE MAGIC ==== */ 449 450 function goMix(filter, sortby, order, $cont, config){ 451 452 // WE ARE NOW MIXING 453 454 clearInterval(config.failsafe); 455 config.mixing = true; 456 457 // APPLY ARGS TO CONFIG 458 459 config.filter = filter; 460 461 // FIRE "ONMIXSTART" CALLBACK 462 463 if(typeof config.onMixStart == 'function') { 464 var output = config.onMixStart.call(this, config); 465 466 // UPDATE CONFIG IF DATA RETURNED 467 468 config = output ? output : config; 469 }; 470 471 // SHORT LOCAL VARS 472 473 var speed = config.transitionSpeed; 474 475 // REBUILD TRANSITION AND PERSPECTIVE OBJECTS 476 477 for(var i = 0; i<2; i++){ 478 var a = i==0 ? a = config.prefix : ''; 479 config.transition[a+'transition'] = 'all '+speed+'ms linear'; 480 config.transition[a+'transform'] = a+'translate3d(0,0,0)'; 481 config.perspective[a+'perspective'] = config.perspectiveDistance+'px'; 482 config.perspective[a+'perspective-origin'] = config.perspectiveOrigin; 483 }; 484 485 // CACHE TARGET ELEMENTS FOR QUICK ACCESS 486 487 var mixSelector = config.targetSelector, 488 $targets = $cont.find(mixSelector); 489 490 // ADD DATA OBJECT TO EACH TARGET 491 492 $targets.each(function(){ 493 this.data = {}; 494 }); 495 496 // RE-DEFINE CONTAINER INCASE NOT IMMEDIATE PARENT OF TARGET ELEMENTS 497 498 var $par = $targets.parent(); 499
500 // ADD PERSPECTIVE TO CONTAINER 501 502 $par.css(config.perspective); 503 504 // SETUP EASING 505 506 config.easingFallback = 'ease-in-out'; 507 if(config.easing == 'smooth')config.easing = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'; 508 if(config.easing == 'snap')config.easing = 'cubic-bezier(0.77, 0, 0.175, 1)'; 509 if(config.easing == 'windback'){ 510 config.easing = 'cubic-bezier(0.175, 0.885, 0.320, 1.275)', 511 config.easingFallback = 'cubic-bezier(0.175, 0.885, 0.320, 1)'; // Fall-back for old webkit, with no values > 1 or < 1 512 }; 513 if(config.easing == 'windup'){ 514 config.easing = 'cubic-bezier(0.6, -0.28, 0.735, 0.045)', 515 config.easingFallback = 'cubic-bezier(0.6, 0.28, 0.735, 0.045)'; 516 }; 517 518 // USE LIST SPECIFIC EFFECTS IF DECLARED 519 520 var effectsOut = config.layoutMode == 'list' && config.listEffects != null ? config.listEffects : config.effects; 521 522 // BUILD EFFECTS STRINGS & SKIP IF IE8 523 524 if (Array.prototype.indexOf){ 525 config.fade = effectsOut.indexOf('fade') > -1 ? '0' : ''; 526 config.scale = effectsOut.indexOf('scale') > -1 ? 'scale(.01)' : ''; 527 config.rotateZ = effectsOut.indexOf('rotateZ') > -1 ? 'rotate(180deg)' : ''; 528 config.rotateY = effectsOut.indexOf('rotateY') > -1 ? 'rotateY(90deg)' : ''; 529 config.rotateX = effectsOut.indexOf('rotateX') > -1 ? 'rotateX(90deg)' : ''; 530 config.blur = effectsOut.indexOf('blur') > -1 ? 'blur(8px)' : ''; 531 config.grayscale = effectsOut.indexOf('grayscale') > -1 ? 'grayscale(100%)' : ''; 532 }; 533 534 // DECLARE NEW JQUERY OBJECTS FOR GROUPING 535 536 var $show = $(), 537 $hide = $(), 538 filterArray = [], 539 multiDimensional = false;
540 541 // BUILD FILTER ARRAY(S) 542 543 if(typeof filter === 'string'){ 544 545 // SINGLE DIMENSIONAL FILTERING 546 547 filterArray = buildFilterArray(filter); 548 549 } else { 550 551 // MULTI DIMENSIONAL FILTERING 552 553 multiDimensional = true; 554 555 $.each(filter,function(i){ 556 filterArray[i] = buildFilterArray(this); 557 }); 558 }; 559 560 // "OR" LOGIC (DEFAULT) 561 562 if(config.filterLogic == 'or'){ 563 564 if(filterArray[0] == '') filterArray.shift(); // IF FIRST ITEM IN ARRAY IS AN EMPTY SPACE, DELETE 565 566 // IF NO ELEMENTS ARE DESIRED THEN HIDE ALL VISIBLE ELEMENTS 567 568 if(filterArray.length < 1){ 569 570 $hide = $hide.add($cont.find(mixSelector+':visible')); 571 572 } else { 573 574 // ELSE CHECK EACH TARGET ELEMENT FOR ANY FILTER CATEGORY: 575 576 $targets.each(function(){ 577 var $t = $(this); 578 if(!multiDimensional){ 579 // IF HAS ANY FILTER, ADD TO "SHOW" OBJECT 580 if($t.is('.'+filterArray.join(', .'))){ 581 $show = $show.add($t); 582 // ELSE IF HAS NO FILTERS, ADD TO "HIDE" OBJECT 583 } else { 584 $hide = $hide.add($t); 585 }; 586 } else { 587 588 var pass = 0; 589 // FOR EACH DIMENSION 590 591 $.each(filterArray,function(i){ 592 if(this.length){ 593 if($t.is('.'+this.join(', .'))){ 594 pass++ 595 }; 596 } else if(pass > 0){ 597 pass++; 598 }; 599 }); 600 // IF PASSES ALL DIMENSIONS, SHOW 601 if(pass == filterArray.length){ 602 $show = $show.add($t); 603 // ELSE HIDE 604 } else { 605 $hide = $hide.add($t); 606 }; 607 }; 608 }); 609 610 }; 611 612 } else { 613 614 // "AND" LOGIC 615 616 // ADD "MIX_SHOW" CLASS TO ELEMENTS THAT HAVE ALL FILTERS 617 618 $show = $show.add($par.find(mixSelector+'.'+filterArray.join('.'))); 619 620 // ADD "MIX_HIDE" CLASS TO EVERYTHING ELSE 621 622 $hide = $hide.add($par.find(mixSelector+':not(.'+filterArray.join('.')+'):visible')); 623 }; 624 625 // GET TOTAL NUMBER OF ELEMENTS TO SHOW 626 627 var total = $show.length; 628 629 // DECLARE NEW JQUERY OBJECTS 630 631 var $tohide = $(), 632 $toshow = $(), 633 $pre = $(); 634 635 // FOR ELEMENTS TO BE HIDDEN, IF NOT ALREADY HIDDEN THEN ADD TO OBJECTS "TOHIDE" AND "PRE" 636 // TO INDICATE PRE-EXISTING ELEMENTS TO BE HIDDEN 637 638 $hide.each(function(){ 639 var $t = $(this); 640 if($t.css('display') != 'none'){ 641 $tohide = $tohide.add($t); 642 $pre = $pre.add($t); 643 }; 644 }); 645 646 // IF ALL ELEMENTS ARE ALREADY SHOWN AND THERE IS NOTHING TO HIDE, AND NOT PERFORMING A LAYOUT CHANGE OR SORT: 647 648 if($show.filter(':visible').length == total && !$tohide.length && !sortby){ 649 650 if(config.origLayout == config.layoutMode){ 651 652 // THEN CLEAN UP AND GO HOME 653 654 resetFilter(); 655 return false; 656 } else { 657 658 // IF ONLY ONE ITEM AND CHANGING FORM GRID TO LIST, MOST LIKELY POSITION WILL NOT CHANGE SO WE'RE DONE 659 660 if($show.length == 1){ 661 662 if(config.layoutMode == 'list'){ 663 $cont.addClass(config.listClass); 664 $cont.removeClass(config.gridClass); 665 $pre.css('display',config.targetDisplayList); 666 } else { 667 $cont.addClass(config.gridClass); 668 $cont.removeClass(config.listClass); 669 $pre.css('display',config.targetDisplayGrid); 670 }; 671 672 // THEN CLEAN UP AND GO HOME 673 674 resetFilter(); 675 return false; 676 } 677 }; 678 }; 679 680 // GET CONTAINER'S STARTING HEIGHT 681 682 config.origHeight = $par.height(); 683 684 // IF THERE IS SOMETHING TO BE SHOWN: 685 686 if($show.length){ 687 688 // REMOVE "FAIL CLASS" FROM CONTAINER IF EXISTS 689 690 $cont.removeClass(config.failClass); 691 692 693 // FOR ELEMENTS TO BE SHOWN, IF NOT ALREADY SHOWN THEN ADD TO OBJECTS "TOSHOW" ELSE ADD CLASS "MIX_PRE" 694 // TO INDICATE PRE-EXISTING ELEMENT 695 696 $show.each(function(){ 697 var $t = $(this); 698 if($t.css('display') == 'none'){ 699 $toshow = $toshow.add($t) 700 } else { 701 $pre = $pre.add($t); 702 }; 703 }); 704 705 // IF NON-ANIMATED LAYOUT MODE TRANSITION: 706 707 if((config.origLayout != config.layoutMode) && config.animateGridList == false){ 708 709 // ADD NEW DISPLAY TYPES, CLEAN UP AND GO HOME 710 711 if(config.layoutMode == 'list'){ 712 $cont.addClass(config.listClass); 713 $cont.removeClass(config.gridClass); 714 $pre.css('display',config.targetDisplayList); 715 } else { 716 $cont.addClass(config.gridClass); 717 $cont.removeClass(config.listClass); 718 $pre.css('display',config.targetDisplayGrid); 719 }; 720 721 resetFilter(); 722 return false;
723 }; 724 725 // IF IE, FUCK OFF, AND THEN CLEAN UP AND GO HOME 726 727 if(!window.atob){ 728 resetFilter(); 729 return false; 730 }; 731 732 // OVERRIDE ANY EXISTING TRANSITION TIMING FOR CALCULATIONS 733 734 $targets.css(config.clean); 735 736 // FOR EACH PRE-EXISTING ELEMENT, ADD STARTING POSITION TO 'ORIGPOS' ARRAY 737 738 $pre.each(function(){ 739 this.data.origPos = $(this).offset(); 740 }); 741 742 // TEMPORARILY SHOW ALL ELEMENTS TO SHOW (THAT ARE NOT ALREADY SHOWN), WITHOUT HIDING ELEMENTS TO HIDE 743 // AND ADD/REMOVE GRID AND LIST CLASSES FROM CONTAINER 744 745 if(config.layoutMode == 'list'){ 746 $cont.addClass(config.listClass); 747 $cont.removeClass(config.gridClass); 748 $toshow.css('display',config.targetDisplayList); 749 } else { 750 $cont.addClass(config.gridClass); 751 $cont.removeClass(config.listClass); 752 $toshow.css('display',config.targetDisplayGrid); 753 }; 754 755 // FOR EACH ELEMENT NOW SHOWN, ADD ITS INTERMEDIATE POSITION TO 'SHOWINTERPOS' ARRAY 756 757 $toshow.each(function(){ 758 this.data.showInterPos = $(this).offset(); 759 }); 760 761 // FOR EACH ELEMENT TO BE HIDDEN, BUT NOT YET HIDDEN, AND NOW MOVED DUE TO SHOWN ELEMENTS, 762 // ADD ITS INTERMEDIATE POSITION TO 'HIDEINTERPOS' ARRAY 763 764 $tohide.each(function(){ 765 this.data.hideInterPos = $(this).offset(); 766 }); 767 768 // FOR EACH PRE-EXISTING ELEMENT, NOW MOVED DUE TO SHOWN ELEMENTS, ADD ITS POSITION TO 'PREINTERPOS' ARRAY 769 770 $pre.each(function(){ 771 this.data.preInterPos = $(this).offset(); 772 }); 773 774 // SET DISPLAY PROPERTY OF PRE-EXISTING ELEMENTS INCASE WE ARE CHANGING LAYOUT MODE 775 776 if(config.layoutMode == 'list'){ 777 $pre.css('display',config.targetDisplayList); 778 } else { 779 $pre.css('display',config.targetDisplayGrid); 780 }; 781 782 // IF A SORT ARGUMENT HAS BEEN SENT, RUN SORT FUNCTION SO OBJECTS WILL MOVE TO THEIR FINAL ORDER 783 784 if(sortby){ 785 sort(sortby, order, $cont, config); 786 }; 787 788 // IF VISIBLE SORT ORDER IS THE SAME (WHICH WOULD NOT TRIGGER A TRANSITION EVENT) 789 790 if(sortby && compareArr(config.origSort, config.checkSort)){ 791 792 // THEN CLEAN UP AND GO HOME 793 resetFilter(); 794 return false; 795 }; 796 797 // TEMPORARILY HIDE ALL SHOWN ELEMENTS TO HIDE 798 799 $tohide.hide(); 800 801 // FOR EACH ELEMENT TO SHOW, AND NOW MOVED DUE TO HIDDEN ELEMENTS BEING REMOVED, 802 // ADD ITS POSITION TO 'FINALPOS' ARRAY 803 804 $toshow.each(function(i){ 805 this.data.finalPos = $(this).offset(); 806 }); 807 808 // FOR EACH PRE-EXISTING ELEMENT NOW MOVED DUE TO HIDDEN ELEMENTS BEING REMOVED, 809 // ADD ITS POSITION TO 'FINALPREPOS' ARRAY 810 811 $pre.each(function(){ 812 this.data.finalPrePos = $(this).offset(); 813 }); 814 815 // SINCE WE ARE IN OUT FINAL STATE, GET NEW HEIGHT OF CONTAINER 816 817 config.newHeight = $par.height(); 818 819 // IF A SORT ARGUMENT AS BEEN SENT, RUN SORT FUNCTION 'RESET' TO MOVE ELEMENTS BACK TO THEIR STARTING ORDER 820 821 if(sortby){ 822 sort('reset', null, $cont, config); 823 }; 824 825 // RE-HIDE ALL ELEMENTS TEMPORARILY SHOWN 826 827 $toshow.hide(); 828 829 // SET DISPLAY PROPERTY OF PRE-EXISTING ELEMENTS BACK TO THEIR 830 // ORIGINAL PROPERTY, INCASE WE ARE CHANGING LAYOUT MODE 831 832 $pre.css('display',config.origDisplay); 833 834 // ADD/REMOVE GRID AND LIST CLASSES FROM CONTAINER 835 836 if(config.origDisplay == 'block'){ 837 $cont.addClass(config.listClass); 838 $toshow.css('display', config.targetDisplayList); 839 } else { 840 $cont.removeClass(config.listClass); 841 $toshow.css('display', config.targetDisplayGrid); 842 }; 843 844 // IF WE ARE ANIMATING CONTAINER, RESET IT TO ITS STARTING HEIGHT 845 846 if(config.resizeContainer)$par.css('height', config.origHeight+'px'); 847 848 // ADD TRANSFORMS TO ALL ELEMENTS TO SHOW 849 850 var toShowCSS = {}; 851 852 for(var i = 0; i<2; i++){ 853 var a = i==0 ? a = config.prefix : ''; 854 toShowCSS[a+'transform'] = config.scale+' '+config.rotateX+' '+config.rotateY+' '+config.rotateZ; 855 toShowCSS[a+'filter'] = config.blur+' '+config.grayscale; 856 }; 857 858 $toshow.css(toShowCSS); 859 860 // FOR EACH PRE-EXISTING ELEMENT, SUBTRACT ITS INTERMEDIATE POSITION FROM ITS ORIGINAL POSITION 861 // TO GET ITS STARTING OFFSET 862 863 $pre.each(function(){ 864 var data = this.data, 865 $t = $(this); 866 867 if ($t.hasClass('mix_tohide')){ 868 data.preTX = data.origPos.left - data.hideInterPos.left; 869 data.preTY = data.origPos.top - data.hideInterPos.top; 870 } else { 871 data.preTX = data.origPos.left - data.preInterPos.left; 872 data.preTY = data.origPos.top - data.preInterPos.top; 873 }; 874 var preCSS = {}; 875 for(var i = 0; i<2; i++){ 876 var a = i==0 ? a = config.prefix : ''; 877 preCSS[a+'transform'] = 'translate('+data.preTX+'px,'+data.preTY+'px)'; 878 }; 879 880 $t.css(preCSS); 881 }); 882 883 // ADD/REMOVE GRID AND LIST CLASSES FROM CONTAINER 884 885 if(config.layoutMode == 'list'){ 886 $cont.addClass(config.listClass); 887 $cont.removeClass(config.gridClass); 888 } else { 889 $cont.addClass(config.gridClass); 890 $cont.removeClass(config.listClass); 891 }; 892 893 // WRAP ANIMATION FUNCTIONS IN 10ms TIMEOUT TO PREVENT RACE CONDITION 894
895 var delay = setTimeout(function(){ 896 897 // APPLY TRANSITION TIMING TO CONTAINER, AND BEGIN ANIMATION TO NEW HEIGHT 898 899 if(config.resizeContainer){ 900 var containerCSS = {}; 901 for(var i = 0; i<2; i++){ 902 var a = i==0 ? a = config.prefix : ''; 903 containerCSS[a+'transition'] = 'all '+speed+'ms ease-in-out'; 904 containerCSS['height'] = config.newHeight+'px'; 905 }; 906 $par.css(containerCSS); 907 }; 908 909 // BEGIN FADING IN/OUT OF ALL ELEMENTS TO SHOW/HIDE 910 $tohide.css('opacity',config.fade); 911 $toshow.css('opacity',1); 912 913 // FOR EACH ELEMENT BEING SHOWN, CALCULATE ITS TRAJECTORY BY SUBTRACTING 914 // ITS INTERMEDIATE POSITION FROM ITS FINAL POSITION. 915 // ALSO ADD SPEED AND EASING 916 917 $toshow.each(function(){ 918 var data = this.data; 919 data.tX = data.finalPos.left - data.showInterPos.left; 920 data.tY = data.finalPos.top - data.showInterPos.top; 921 922 var toShowCSS = {}; 923 for(var i = 0; i<2; i++){ 924 var a = i==0 ? a = config.prefix : ''; 925 toShowCSS[a+'transition-property'] = a+'transform, '+a+'filter, opacity'; 926 toShowCSS[a+'transition-timing-function'] = config.easing+', linear, linear'; 927 toShowCSS[a+'transition-duration'] = speed+'ms'; 928 toShowCSS[a+'transition-delay'] = '0'; 929 toShowCSS[a+'transform'] = 'translate('+data.tX+'px,'+data.tY+'px)'; 930 toShowCSS[a+'filter'] = 'none'; 931 }; 932 933 $(this).css('-webkit-transition', 'all '+speed+'ms '+config.easingFallback).css(toShowCSS); 934 }); 935 936 // FOR EACH PRE-EXISTING ELEMENT, IF IT HAS A FINAL POSITION, CALCULATE 937 // ITS TRAJETORY BY SUBTRACTING ITS INTERMEDIATE POSITION FROM ITS FINAL POSITION. 938 // ALSO ADD SPEED AND EASING 939 940 $pre.each(function(){ 941 var data = this.data 942 data.tX = data.finalPrePos.left != 0 ? data.finalPrePos.left - data.preInterPos.left : 0; 943 data.tY = data.finalPrePos.left != 0 ? data.finalPrePos.top - data.preInterPos.top : 0; 944 945 var preCSS = {}; 946 for(var i = 0; i<2; i++){ 947 var a = i==0 ? a = config.prefix : ''; 948 preCSS[a+'transition'] = 'all '+speed+'ms '+config.easing; 949 preCSS[a+'transform'] = 'translate('+data.tX+'px,'+data.tY+'px)'; 950 }; 951 952 $(this).css('-webkit-transition', 'all '+speed+'ms '+config.easingFallback).css(preCSS); 953 }); 954 955 // BEGIN TRANSFORMS ON ALL ELEMENTS TO BE HIDDEN 956 957 var toHideCSS = {}; 958 for(var i = 0; i<2; i++){ 959 var a = i==0 ? a = config.prefix : ''; 960 toHideCSS[a+'transition'] = 'all '+speed+'ms '+config.easing+', '+a+'filter '+speed+'ms linear, opacity '+speed+'ms linear'; 961 toHideCSS[a+'transform'] = config.scale+' '+config.rotateX+' '+config.rotateY+' '+config.rotateZ; 962 toHideCSS[a+'filter'] = config.blur+' '+config.grayscale; 963 toHideCSS['opacity'] = config.fade; 964 }; 965 966 $tohide.css(toHideCSS); 967 968 // ALL ANIMATIONS HAVE NOW BEEN STARTED, NOW LISTEN FOR TRANSITION END: 969 970 $par.bind('webkitTransitionEnd transitionend otransitionend oTransitionEnd',function(e){ 971 972 if (e.originalEvent.propertyName.indexOf('transform') > -1 || e.originalEvent.propertyName.indexOf('opacity') > -1){ 973 974 if(mixSelector.indexOf('.') > -1){ 975 976 // IF MIXSELECTOR IS A CLASS NAME 977 978 if($(e.target).hasClass(mixSelector.replace('.',''))){ 979 resetFilter(); 980 }; 981 982 } else { 983 984 // IF MIXSELECTOR IS A TAG 985 986 if($(e.target).is(mixSelector)){ 987 resetFilter(); 988 }; 989 990 }; 991 992 }; 993 }); 994 995 },10); 996 997 // LAST RESORT EMERGENCY FAILSAFE 998 999 config.failsafe = setTimeout(function(){ 1000 if(config.mixing){ 1001 resetFilter(); 1002 }; 1003 }, speed + 400); 1004 1005 } else { 1006 1007 // ELSE IF NOTHING TO SHOW, AND EVERYTHING TO BE HIDDEN 1008 1009 // IF WE ARE RESIZING CONTAINER, SET ITS STARTING HEIGHT 1010 1011 if(config.resizeContainer)$par.css('height', config.origHeight+'px'); 1012 1013 // IF IE, FUCK OFF, AND THEN GO HOME 1014 1015 if(!window.atob){ 1016 resetFilter(); 1017 return false; 1018 }; 1019 1020 // GROUP ALL ELEMENTS TO HIDE INTO JQUERY OBJECT 1021 1022 $tohide = $hide; 1023 1024 // WRAP ANIMATION FUNCTIONS IN A 10ms DELAY TO PREVENT RACE CONDITION 1025
1026 var delay = setTimeout(function(){ 1027 1028 // APPLY PERSPECTIVE TO CONTAINER 1029 1030 $par.css(config.perspective); 1031 1032 // APPLY TRANSITION TIMING TO CONTAINER, AND BEGIN ANIMATION TO NEW HEIGHT 1033 1034 if(config.resizeContainer){ 1035 var containerCSS = {}; 1036 for(var i = 0; i<2; i++){ 1037 var a = i==0 ? a = config.prefix : ''; 1038 containerCSS[a+'transition'] = 'height '+speed+'ms ease-in-out'; 1039 containerCSS['height'] = config.minHeight+'px'; 1040 }; 1041 $par.css(containerCSS); 1042 }; 1043 1044 // APPLY TRANSITION TIMING TO ALL TARGET ELEMENTS 1045 1046 $targets.css(config.transition); 1047 1048 // GET TOTAL NUMBER OF ELEMENTS TO HIDE 1049 1050 var totalHide = $hide.length; 1051 1052 // IF SOMETHING TO HIDE: 1053 1054 if(totalHide){ 1055 1056 // BEGIN TRANSFORMS ON ALL ELEMENTS TO BE HIDDEN 1057 1058 var toHideCSS = {}; 1059 for(var i = 0; i<2; i++){ 1060 var a = i==0 ? a = config.prefix : ''; 1061 toHideCSS[a+'transform'] = config.scale+' '+config.rotateX+' '+config.rotateY+' '+config.rotateZ; 1062 toHideCSS[a+'filter'] = config.blur+' '+config.grayscale; 1063 toHideCSS['opacity'] = config.fade; 1064 }; 1065 1066 $tohide.css(toHideCSS); 1067 1068 // ALL ANIMATIONS HAVE NOW BEEN STARTED, NOW LISTEN FOR TRANSITION END: 1069 1070 $par.bind('webkitTransitionEnd transitionend otransitionend oTransitionEnd',function(e){ 1071 if (e.originalEvent.propertyName.indexOf('transform') > -1 || e.originalEvent.propertyName.indexOf('opacity') > -1){ 1072 $cont.addClass(config.failClass); 1073 resetFilter(); 1074 }; 1075 }); 1076 1077 } else { 1078 1079 // ELSE, WE'RE DONE MIXING 1080 1081 config.mixing = false; 1082 }; 1083 1084 }, 10); 1085 }; 1086 1087 // CLEAN UP AND RESET FUNCTION 1088 1089 function resetFilter(){ 1090 1091 // UNBIND TRANSITION END EVENTS FROM CONTAINER 1092 1093 $par.unbind('webkitTransitionEnd transitionend otransitionend oTransitionEnd'); 1094 1095 // IF A SORT ARGUMENT HAS BEEN SENT, SORT ELEMENTS TO THEIR FINAL ORDER 1096 1097 if(sortby){ 1098 sort(sortby, order, $cont, config); 1099 }; 1100 1101 // EMPTY SORTING ARRAYS 1102 1103 config.startOrder = [], config.newOrder = [], config.origSort = [], config.checkSort = []; 1104 1105 // REMOVE INLINE STYLES FROM ALL TARGET ELEMENTS AND SLAM THE BRAKES ON 1106 1107 $targets.removeStyle( 1108 config.prefix+'filter, filter, '+config.prefix+'transform, transform, opacity, display' 1109 ).css(config.clean).removeAttr('data-checksum'); 1110 1111 // BECAUSE IE SUCKS 1112 1113 if(!window.atob){ 1114 $targets.css({ 1115 display: 'none', 1116 opacity: '0' 1117 }); 1118 }; 1119 1120 // REMOVE HEIGHT FROM CONTAINER ONLY IF RESIZING 1121 1122 var remH = config.resizeContainer ? 'height' : ''; 1123 1124 // REMOVE INLINE STYLES FROM CONTAINER 1125 1126 $par.removeStyle( 1127 config.prefix+'transition, transition, '+config.prefix+'perspective, perspective, '+config.prefix+'perspective-origin, perspective-origin, '+remH 1128 ); 1129 1130 // ADD FINAL DISPLAY PROPERTIES AND OPACITY TO ALL SHOWN ELEMENTS 1131 // CACHE CURRENT LAYOUT MODE & SORT FOR NEXT MIX 1132 1133 if(config.layoutMode == 'list'){ 1134 $show.css({display:config.targetDisplayList, opacity:'1'}); 1135 config.origDisplay = config.targetDisplayList; 1136 } else { 1137 $show.css({display:config.targetDisplayGrid, opacity:'1'}); 1138 config.origDisplay = config.targetDisplayGrid; 1139 }; 1140 config.origLayout = config.layoutMode; 1141 1142 var wait = setTimeout(function(){ 1143 1144 // LET GO OF THE BRAKES 1145 1146 $targets.removeStyle(config.prefix+'transition, transition'); 1147 1148 // WE'RE DONE MIXING 1149 1150 config.mixing = false; 1151 1152 // FIRE "ONMIXEND" CALLBACK 1153 1154 if(typeof config.onMixEnd == 'function') { 1155 var output = config.onMixEnd.call(this, config); 1156 1157 // UPDATE CONFIG IF DATA RETURNED 1158 1159 config = output ? output : config; 1160 }; 1161 }); 1162 }; 1163 }; 1164 1165 // SORT FUNCTION 1166 1167 function sort(sortby, order, $cont, config){ 1168 1169 // COMPARE BY ATTRIBUTE 1170 1171 function compare(a,b) { 1172 var sortAttrA = isNaN(a.attr(sortby) * 1) ? a.attr(sortby).toLowerCase() : a.attr(sortby) * 1, 1173 sortAttrB = isNaN(b.attr(sortby) * 1) ? b.attr(sortby).toLowerCase() : b.attr(sortby) * 1; 1174 if (sortAttrA < sortAttrB) 1175 return -1; 1176 if (sortAttrA > sortAttrB) 1177 return 1; 1178 return 0; 1179 }; 1180
1181 // REBUILD DOM 1182 1183 function rebuild(element){ 1184 if(order == 'asc'){ 1185 $sortWrapper.prepend(element).prepend(' '); 1186 } else { 1187 $sortWrapper.append(element).append(' '); 1188 }; 1189 }; 1190 1191 // RANDOMIZE ARRAY 1192 1193 function arrayShuffle(oldArray){ 1194 var newArray = oldArray.slice(); 1195 var len = newArray.length; 1196 var i = len; 1197 while (i--){ 1198 var p = parseInt(Math.random()*len); 1199 var t = newArray[i]; 1200 newArray[i] = newArray[p]; 1201 newArray[p] = t; 1202 }; 1203 return newArray; 1204 }; 1205 1206 // SORT 1207 1208 $cont.find(config.targetSelector).wrapAll('<div class="mix_sorter"/>'); 1209 1210 var $sortWrapper = $cont.find('.mix_sorter'); 1211 1212 if(!config.origSort.length){ 1213 $sortWrapper.find(config.targetSelector+':visible').each(function(){ 1214 $(this).wrap('<s/>'); 1215 config.origSort.push($(this).parent().html().replace(/\s+/g, '')); 1216 $(this).unwrap(); 1217 }); 1218 }; 1219 1220 1221 1222 $sortWrapper.empty(); 1223 1224 if(sortby == 'reset'){ 1225 $.each(config.startOrder,function(){ 1226 $sortWrapper.append(this).append(' '); 1227 }); 1228 } else if(sortby == 'default'){ 1229 $.each(config.origOrder,function(){ 1230 rebuild(this); 1231 }); 1232 } else if(sortby == 'random'){ 1233 if(!config.newOrder.length){ 1234 config.newOrder = arrayShuffle(config.startOrder); 1235 }; 1236 $.each(config.newOrder,function(){ 1237 $sortWrapper.append(this).append(' '); 1238 }); 1239 } else if(sortby == 'custom'){ 1240 $.each(order, function(){ 1241 rebuild(this); 1242 }); 1243 } else { 1244 // SORT BY ATTRIBUTE 1245 1246 if(typeof config.origOrder[0].attr(sortby) === 'undefined'){ 1247 console.log('No such attribute found. Terminating'); 1248 return false; 1249 }; 1250 1251 if(!config.newOrder.length){ 1252 $.each(config.origOrder,function(){ 1253 config.newOrder.push($(this)); 1254 }); 1255 config.newOrder.sort(compare); 1256 }; 1257 $.each(config.newOrder,function(){ 1258 rebuild(this); 1259 }); 1260 1261 }; 1262 config.checkSort = []; 1263 $sortWrapper.find(config.targetSelector+':visible').each(function(i){ 1264 var $t = $(this); 1265 if(i == 0){ 1266 1267 // PREVENT COMPARE RETURNING FALSE POSITIVES ON ELEMENTS WITH NO CLASS/ATTRIBUTES 1268 1269 $t.attr('data-checksum','1'); 1270 }; 1271 $t.wrap('<s/>'); 1272 config.checkSort.push($t.parent().html().replace(/\s+/g, '')); 1273 $t.unwrap(); 1274 }); 1275 1276 $cont.find(config.targetSelector).unwrap(); 1277 }; 1278 1279 // FIND VENDOR PREFIX 1280 1281 function prefix(el) { 1282 var prefixes = ["Webkit", "Moz", "O", "ms"]; 1283 for (var i = 0; i < prefixes.length; i++){ 1284 if (prefixes[i] + "Transition" in el.style){ 1285 return prefixes[i]; 1286 }; 1287 }; 1288 return "transition" in el.style ? "" : false; 1289 }; 1290 1291 // REMOVE SPECIFIC STYLES 1292 1293 $.fn.removeStyle = function(style){ 1294 return this.each(function(){ 1295 var obj = $(this); 1296 style = style.replace(/\s+/g, ''); 1297 var styles = style.split(','); 1298 $.each(styles,function(){ 1299 1300 var search = new RegExp(this.toString() + '[^;]+;?', 'g'); 1301 obj.attr('style', function(i, style){ 1302 if(style) return style.replace(search, ''); 1303 }); 1304 }); 1305 }); 1306 }; 1307 1308 // COMPARE ARRAYS 1309 1310 function compareArr(a,b){ 1311 if (a.length != b.length) return false; 1312 for (var i = 0; i < b.length; i++){ 1313 if (a[i].compare) { 1314 if (!a[i].compare(b[i])) return false; 1315 }; 1316 if (a[i] !== b[i]) return false; 1317 }; 1318 return true; 1319 }; 1320 1321 // BUILD FILTER ARRAY(S) 1322 1323 function buildFilterArray(str){ 1324 // CLEAN FILTER STRING 1325 str = str.replace(/\s{2,}/g, ' '); 1326 // FOR EACH PEROID SEPERATED CLASS NAME, ADD STRING TO FILTER ARRAY 1327 var arr = str.split(' '); 1328 // IF ALL, REPLACE WITH MIX_ALL 1329 $.each(arr,function(i){ 1330 if(this == 'all')arr[i] = 'mix_all'; 1331 }); 1332 if(arr[0] == "")arr.shift(); 1333 return arr; 1334 }; 1335 1336 1337})(jQuery);
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.