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