/* * MIXITUP - A CSS3 and JQuery Filter & Sort Plugin * Version: 1.5.4 * License: Creative Commons Attribution-NoDerivs 3.0 Unported - CC BY-ND 3.0 * http://creativecommons.org/licenses/by-nd/3.0/ * This software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder. * Author: Patrick Kunka * Copyright 2012-2013 Patrick Kunka, Barrel LLC, All Rights Reserved * * http://mixitup.io */ (function($){ // DECLARE METHODS var methods = { // "INIT" METHOD init: function(settings){ return this.each(function(){ // BUILD CONFIG OBJECT var config = { // PUBLIC PROPERTIES targetSelector : '.mix', filterSelector : '.filter', sortSelector : '.sort', buttonEvent: 'click', effects : ['fade', 'scale'], listEffects : null, easing : 'smooth', layoutMode: 'grid', targetDisplayGrid : 'inline-block', targetDisplayList: 'block', listClass : '', gridClass : '', transitionSpeed : 600, showOnLoad : 'all', sortOnLoad : false, multiFilter : false, filterLogic : 'or', resizeContainer : true, minHeight : 0, failClass : 'fail', perspectiveDistance : '3000', perspectiveOrigin : '50% 50%', animateGridList : true, onMixLoad: null, onMixStart : null, onMixEnd : null, // MISC container : null, origOrder : [], startOrder : [], newOrder : [], origSort: [], checkSort: [], filter : '', mixing : false, origDisplay : '', origLayout: '', origHeight : 0, newHeight : 0, isTouch : false, resetDelay : 0, failsafe : null, // CSS prefix : '', easingFallback : 'ease-in-out', transition : {}, perspective : {}, clean : {}, fade : '1', scale : '', rotateX : '', rotateY : '', rotateZ : '', blur : '', grayscale : '' }; if(settings){ $.extend(config, settings); }; // ADD CONFIG OBJECT TO CONTAINER OBJECT PER INSTANTIATION this.config = config; // DETECT TOUCH $.support.touch = 'ontouchend' in document; if ($.support.touch) { config.isTouch = true; config.resetDelay = 350; }; // LOCALIZE CONTAINER config.container = $(this); var $cont = config.container; // GET VENDOR PREFIX config.prefix = prefix($cont[0]); config.prefix = config.prefix ? '-'+config.prefix.toLowerCase()+'-' : ''; // CACHE 'DEFAULT' SORTING ORDER $cont.find(config.targetSelector).each(function(){ config.origOrder.push($(this)); }); // PERFORM SORT ON LOAD if(config.sortOnLoad){ var sortby, order; if($.isArray(config.sortOnLoad)){ sortby = config.sortOnLoad[0], order = config.sortOnLoad[1]; $(config.sortSelector+'[data-sort='+config.sortOnLoad[0]+'][data-order='+config.sortOnLoad[1]+']').addClass('active'); } else { $(config.sortSelector+'[data-sort='+config.sortOnLoad+']').addClass('active'); sortby = config.sortOnLoad, config.sortOnLoad = 'desc'; }; sort(sortby, order, $cont, config); }; // BUILD TRANSITION AND PERSPECTIVE OBJECTS for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; config.transition[a+'transition'] = 'all '+config.transitionSpeed+'ms ease-in-out'; config.perspective[a+'perspective'] = config.perspectiveDistance+'px'; config.perspective[a+'perspective-origin'] = config.perspectiveOrigin; }; // BUILD TRANSITION CLEANER for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; config.clean[a+'transition'] = 'none'; }; // CHOOSE GRID OR LIST if(config.layoutMode == 'list'){ $cont.addClass(config.listClass); config.origDisplay = config.targetDisplayList; } else { $cont.addClass(config.gridClass); config.origDisplay = config.targetDisplayGrid; }; config.origLayout = config.layoutMode; // PARSE 'SHOWONLOAD' var showOnLoadArray = config.showOnLoad.split(' '); // GIVE ACTIVE FILTER ACTIVE CLASS $.each(showOnLoadArray, function(){ $(config.filterSelector+'[data-filter="'+this+'"]').addClass('active'); }); // RENAME "ALL" CATEGORY TO "MIX_ALL" $cont.find(config.targetSelector).addClass('mix_all'); if(showOnLoadArray[0] == 'all'){ showOnLoadArray[0] = 'mix_all', config.showOnLoad = 'mix_all'; }; // FADE IN 'SHOWONLOAD' var $showOnLoad = $(); $.each(showOnLoadArray, function(){ $showOnLoad = $showOnLoad.add($('.'+this)) }); $showOnLoad.each(function(){ var $t = $(this); if(config.layoutMode == 'list'){ $t.css('display',config.targetDisplayList); } else { $t.css('display',config.targetDisplayGrid); }; $t.css(config.transition); }); // WRAP FADE-IN TO PREVENT RACE CONDITION var delay = setTimeout(function(){ config.mixing = true; $showOnLoad.css('opacity','1'); // CLEAN UP var reset = setTimeout(function(){ if(config.layoutMode == 'list'){ $showOnLoad.removeStyle(config.prefix+'transition, transition').css({ display: config.targetDisplayList, opacity: 1 }); } else { $showOnLoad.removeStyle(config.prefix+'transition, transition').css({ display: config.targetDisplayGrid, opacity: 1 }); }; // FIRE "ONMIXLOAD" CALLBACK config.mixing = false; if(typeof config.onMixLoad == 'function') { var output = config.onMixLoad.call(this, config); // UPDATE CONFIG IF DATA RETURNED config = output ? output : config; }; },config.transitionSpeed); },10); // PRESET ACTIVE FILTER config.filter = config.showOnLoad; // BIND SORT CLICK HANDLERS $(config.sortSelector).bind(config.buttonEvent,function(){ if(!config.mixing){ // PARSE SORT ARGUMENTS FROM BUTTON CLASSES var $t = $(this), sortby = $t.attr('data-sort'), order = $t.attr('data-order'); if(!$t.hasClass('active')){ $(config.sortSelector).removeClass('active'); $t.addClass('active'); } else { if(sortby != 'random')return false; }; $cont.find(config.targetSelector).each(function(){ config.startOrder.push($(this)); }); goMix(config.filter,sortby,order,$cont, config); }; }); // BIND FILTER CLICK HANDLERS $(config.filterSelector).bind(config.buttonEvent,function(){ if(!config.mixing){ var $t = $(this); // PARSE FILTER ARGUMENTS FROM BUTTON CLASSES if(config.multiFilter == false){ // SINGLE ACTIVE BUTTON $(config.filterSelector).removeClass('active'); $t.addClass('active'); config.filter = $t.attr('data-filter'); $(config.filterSelector+'[data-filter="'+config.filter+'"]').addClass('active'); } else { // MULTIPLE ACTIVE BUTTONS var thisFilter = $t.attr('data-filter'); if($t.hasClass('active')){ $t.removeClass('active'); // REMOVE FILTER FROM SPACE-SEPERATED STRING var re = new RegExp('(\\s|^)'+thisFilter); config.filter = config.filter.replace(re,''); } else { // ADD FILTER TO SPACE-SEPERATED STRING $t.addClass('active'); config.filter = config.filter+' '+thisFilter; }; }; // GO MIX goMix(config.filter, null, null, $cont, config); }; }); }); }, // "TOGRID" METHOD toGrid: function(){ return this.each(function(){ var config = this.config; if(config.layoutMode != 'grid'){ config.layoutMode = 'grid'; goMix(config.filter, null, null, $(this), config); }; }); }, // "TOLIST" METHOD toList: function(){ return this.each(function(){ var config = this.config; if(config.layoutMode != 'list'){ config.layoutMode = 'list'; goMix(config.filter, null, null, $(this), config); }; }); }, // "FILTER" METHOD filter: function(arg){ return this.each(function(){ var config = this.config; if(!config.mixing){ $(config.filterSelector).removeClass('active'); $(config.filterSelector+'[data-filter="'+arg+'"]').addClass('active'); goMix(arg, null, null, $(this), config); }; }); }, // "SORT" METHOD sort: function(args){ return this.each(function(){ var config = this.config, $t = $(this); if(!config.mixing){ $(config.sortSelector).removeClass('active'); if($.isArray(args)){ var sortby = args[0], order = args[1]; $(config.sortSelector+'[data-sort="'+args[0]+'"][data-order="'+args[1]+'"]').addClass('active'); } else { $(config.sortSelector+'[data-sort="'+args+'"]').addClass('active'); var sortby = args, order = 'desc'; }; $t.find(config.targetSelector).each(function(){ config.startOrder.push($(this)); }); goMix(config.filter,sortby,order, $t, config); }; }); }, // "MULTIMIX" METHOD multimix: function(args){ return this.each(function(){ var config = this.config, $t = $(this); multiOut = { filter: config.filter, sort: null, order: 'desc', layoutMode: config.layoutMode }; $.extend(multiOut, args); if(!config.mixing){ $(config.filterSelector).add(config.sortSelector).removeClass('active'); $(config.filterSelector+'[data-filter="'+multiOut.filter+'"]').addClass('active'); if(typeof multiOut.sort !== 'undefined'){ $(config.sortSelector+'[data-sort="'+multiOut.sort+'"][data-order="'+multiOut.order+'"]').addClass('active'); $t.find(config.targetSelector).each(function(){ config.startOrder.push($(this)); }); }; config.layoutMode = multiOut.layoutMode; goMix(multiOut.filter,multiOut.sort,multiOut.order, $t, config); }; }); }, // "REMIX" METHOD remix: function(arg){ return this.each(function(){ var config = this.config, $t = $(this); config.origOrder = []; $t.find(config.targetSelector).each(function(){ var $th = $(this); $th.addClass('mix_all'); config.origOrder.push($th); }); if(!config.mixing && typeof arg !== 'undefined'){ $(config.filterSelector).removeClass('active'); $(config.filterSelector+'[data-filter="'+arg+'"]').addClass('active'); goMix(arg, null, null, $t, config); }; }); } }; // DECLARE PLUGIN $.fn.mixitup = function(method, arg){ if (methods[method]) { return methods[method].apply( this, Array.prototype.slice.call(arguments,1)); } else if (typeof method === 'object' || ! method){ return methods.init.apply( this, arguments ); }; }; /* ==== THE MAGIC ==== */ function goMix(filter, sortby, order, $cont, config){ // WE ARE NOW MIXING clearInterval(config.failsafe); config.mixing = true; // APPLY ARGS TO CONFIG config.filter = filter; // FIRE "ONMIXSTART" CALLBACK if(typeof config.onMixStart == 'function') { var output = config.onMixStart.call(this, config); // UPDATE CONFIG IF DATA RETURNED config = output ? output : config; }; // SHORT LOCAL VARS var speed = config.transitionSpeed; // REBUILD TRANSITION AND PERSPECTIVE OBJECTS for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; config.transition[a+'transition'] = 'all '+speed+'ms linear'; config.transition[a+'transform'] = a+'translate3d(0,0,0)'; config.perspective[a+'perspective'] = config.perspectiveDistance+'px'; config.perspective[a+'perspective-origin'] = config.perspectiveOrigin; }; // CACHE TARGET ELEMENTS FOR QUICK ACCESS var mixSelector = config.targetSelector, $targets = $cont.find(mixSelector); // ADD DATA OBJECT TO EACH TARGET $targets.each(function(){ this.data = {}; }); // RE-DEFINE CONTAINER INCASE NOT IMMEDIATE PARENT OF TARGET ELEMENTS var $par = $targets.parent(); // ADD PERSPECTIVE TO CONTAINER $par.css(config.perspective); // SETUP EASING config.easingFallback = 'ease-in-out'; if(config.easing == 'smooth')config.easing = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'; if(config.easing == 'snap')config.easing = 'cubic-bezier(0.77, 0, 0.175, 1)'; if(config.easing == 'windback'){ config.easing = 'cubic-bezier(0.175, 0.885, 0.320, 1.275)', config.easingFallback = 'cubic-bezier(0.175, 0.885, 0.320, 1)'; // Fall-back for old webkit, with no values > 1 or < 1 }; if(config.easing == 'windup'){ config.easing = 'cubic-bezier(0.6, -0.28, 0.735, 0.045)', config.easingFallback = 'cubic-bezier(0.6, 0.28, 0.735, 0.045)'; }; // USE LIST SPECIFIC EFFECTS IF DECLARED var effectsOut = config.layoutMode == 'list' && config.listEffects != null ? config.listEffects : config.effects; // BUILD EFFECTS STRINGS & SKIP IF IE8 if (Array.prototype.indexOf){ config.fade = effectsOut.indexOf('fade') > -1 ? '0' : ''; config.scale = effectsOut.indexOf('scale') > -1 ? 'scale(.01)' : ''; config.rotateZ = effectsOut.indexOf('rotateZ') > -1 ? 'rotate(180deg)' : ''; config.rotateY = effectsOut.indexOf('rotateY') > -1 ? 'rotateY(90deg)' : ''; config.rotateX = effectsOut.indexOf('rotateX') > -1 ? 'rotateX(90deg)' : ''; config.blur = effectsOut.indexOf('blur') > -1 ? 'blur(8px)' : ''; config.grayscale = effectsOut.indexOf('grayscale') > -1 ? 'grayscale(100%)' : ''; }; // DECLARE NEW JQUERY OBJECTS FOR GROUPING var $show = $(), $hide = $(), filterArray = [], multiDimensional = false; // BUILD FILTER ARRAY(S) if(typeof filter === 'string'){ // SINGLE DIMENSIONAL FILTERING filterArray = buildFilterArray(filter); } else { // MULTI DIMENSIONAL FILTERING multiDimensional = true; $.each(filter,function(i){ filterArray[i] = buildFilterArray(this); }); }; // "OR" LOGIC (DEFAULT) if(config.filterLogic == 'or'){ if(filterArray[0] == '') filterArray.shift(); // IF FIRST ITEM IN ARRAY IS AN EMPTY SPACE, DELETE // IF NO ELEMENTS ARE DESIRED THEN HIDE ALL VISIBLE ELEMENTS if(filterArray.length < 1){ $hide = $hide.add($cont.find(mixSelector+':visible')); } else { // ELSE CHECK EACH TARGET ELEMENT FOR ANY FILTER CATEGORY: $targets.each(function(){ var $t = $(this); if(!multiDimensional){ // IF HAS ANY FILTER, ADD TO "SHOW" OBJECT if($t.is('.'+filterArray.join(', .'))){ $show = $show.add($t); // ELSE IF HAS NO FILTERS, ADD TO "HIDE" OBJECT } else { $hide = $hide.add($t); }; } else { var pass = 0; // FOR EACH DIMENSION $.each(filterArray,function(i){ if(this.length){ if($t.is('.'+this.join(', .'))){ pass++ }; } else if(pass > 0){ pass++; }; }); // IF PASSES ALL DIMENSIONS, SHOW if(pass == filterArray.length){ $show = $show.add($t); // ELSE HIDE } else { $hide = $hide.add($t); }; }; }); }; } else { // "AND" LOGIC // ADD "MIX_SHOW" CLASS TO ELEMENTS THAT HAVE ALL FILTERS $show = $show.add($par.find(mixSelector+'.'+filterArray.join('.'))); // ADD "MIX_HIDE" CLASS TO EVERYTHING ELSE $hide = $hide.add($par.find(mixSelector+':not(.'+filterArray.join('.')+'):visible')); }; // GET TOTAL NUMBER OF ELEMENTS TO SHOW var total = $show.length; // DECLARE NEW JQUERY OBJECTS var $tohide = $(), $toshow = $(), $pre = $(); // FOR ELEMENTS TO BE HIDDEN, IF NOT ALREADY HIDDEN THEN ADD TO OBJECTS "TOHIDE" AND "PRE" // TO INDICATE PRE-EXISTING ELEMENTS TO BE HIDDEN $hide.each(function(){ var $t = $(this); if($t.css('display') != 'none'){ $tohide = $tohide.add($t); $pre = $pre.add($t); }; }); // IF ALL ELEMENTS ARE ALREADY SHOWN AND THERE IS NOTHING TO HIDE, AND NOT PERFORMING A LAYOUT CHANGE OR SORT: if($show.filter(':visible').length == total && !$tohide.length && !sortby){ if(config.origLayout == config.layoutMode){ // THEN CLEAN UP AND GO HOME resetFilter(); return false; } else { // IF ONLY ONE ITEM AND CHANGING FORM GRID TO LIST, MOST LIKELY POSITION WILL NOT CHANGE SO WE'RE DONE if($show.length == 1){ if(config.layoutMode == 'list'){ $cont.addClass(config.listClass); $cont.removeClass(config.gridClass); $pre.css('display',config.targetDisplayList); } else { $cont.addClass(config.gridClass); $cont.removeClass(config.listClass); $pre.css('display',config.targetDisplayGrid); }; // THEN CLEAN UP AND GO HOME resetFilter(); return false; } }; }; // GET CONTAINER'S STARTING HEIGHT config.origHeight = $par.height(); // IF THERE IS SOMETHING TO BE SHOWN: if($show.length){ // REMOVE "FAIL CLASS" FROM CONTAINER IF EXISTS $cont.removeClass(config.failClass); // FOR ELEMENTS TO BE SHOWN, IF NOT ALREADY SHOWN THEN ADD TO OBJECTS "TOSHOW" ELSE ADD CLASS "MIX_PRE" // TO INDICATE PRE-EXISTING ELEMENT $show.each(function(){ var $t = $(this); if($t.css('display') == 'none'){ $toshow = $toshow.add($t) } else { $pre = $pre.add($t); }; }); // IF NON-ANIMATED LAYOUT MODE TRANSITION: if((config.origLayout != config.layoutMode) && config.animateGridList == false){ // ADD NEW DISPLAY TYPES, CLEAN UP AND GO HOME if(config.layoutMode == 'list'){ $cont.addClass(config.listClass); $cont.removeClass(config.gridClass); $pre.css('display',config.targetDisplayList); } else { $cont.addClass(config.gridClass); $cont.removeClass(config.listClass); $pre.css('display',config.targetDisplayGrid); }; resetFilter(); return false; }; // IF IE, FUCK OFF, AND THEN CLEAN UP AND GO HOME if(!window.atob){ resetFilter(); return false; }; // OVERRIDE ANY EXISTING TRANSITION TIMING FOR CALCULATIONS $targets.css(config.clean); // FOR EACH PRE-EXISTING ELEMENT, ADD STARTING POSITION TO 'ORIGPOS' ARRAY $pre.each(function(){ this.data.origPos = $(this).offset(); }); // TEMPORARILY SHOW ALL ELEMENTS TO SHOW (THAT ARE NOT ALREADY SHOWN), WITHOUT HIDING ELEMENTS TO HIDE // AND ADD/REMOVE GRID AND LIST CLASSES FROM CONTAINER if(config.layoutMode == 'list'){ $cont.addClass(config.listClass); $cont.removeClass(config.gridClass); $toshow.css('display',config.targetDisplayList); } else { $cont.addClass(config.gridClass); $cont.removeClass(config.listClass); $toshow.css('display',config.targetDisplayGrid); }; // FOR EACH ELEMENT NOW SHOWN, ADD ITS INTERMEDIATE POSITION TO 'SHOWINTERPOS' ARRAY $toshow.each(function(){ this.data.showInterPos = $(this).offset(); }); // FOR EACH ELEMENT TO BE HIDDEN, BUT NOT YET HIDDEN, AND NOW MOVED DUE TO SHOWN ELEMENTS, // ADD ITS INTERMEDIATE POSITION TO 'HIDEINTERPOS' ARRAY $tohide.each(function(){ this.data.hideInterPos = $(this).offset(); }); // FOR EACH PRE-EXISTING ELEMENT, NOW MOVED DUE TO SHOWN ELEMENTS, ADD ITS POSITION TO 'PREINTERPOS' ARRAY $pre.each(function(){ this.data.preInterPos = $(this).offset(); }); // SET DISPLAY PROPERTY OF PRE-EXISTING ELEMENTS INCASE WE ARE CHANGING LAYOUT MODE if(config.layoutMode == 'list'){ $pre.css('display',config.targetDisplayList); } else { $pre.css('display',config.targetDisplayGrid); }; // IF A SORT ARGUMENT HAS BEEN SENT, RUN SORT FUNCTION SO OBJECTS WILL MOVE TO THEIR FINAL ORDER if(sortby){ sort(sortby, order, $cont, config); }; // IF VISIBLE SORT ORDER IS THE SAME (WHICH WOULD NOT TRIGGER A TRANSITION EVENT) if(sortby && compareArr(config.origSort, config.checkSort)){ // THEN CLEAN UP AND GO HOME resetFilter(); return false; }; // TEMPORARILY HIDE ALL SHOWN ELEMENTS TO HIDE $tohide.hide(); // FOR EACH ELEMENT TO SHOW, AND NOW MOVED DUE TO HIDDEN ELEMENTS BEING REMOVED, // ADD ITS POSITION TO 'FINALPOS' ARRAY $toshow.each(function(i){ this.data.finalPos = $(this).offset(); }); // FOR EACH PRE-EXISTING ELEMENT NOW MOVED DUE TO HIDDEN ELEMENTS BEING REMOVED, // ADD ITS POSITION TO 'FINALPREPOS' ARRAY $pre.each(function(){ this.data.finalPrePos = $(this).offset(); }); // SINCE WE ARE IN OUT FINAL STATE, GET NEW HEIGHT OF CONTAINER config.newHeight = $par.height(); // IF A SORT ARGUMENT AS BEEN SENT, RUN SORT FUNCTION 'RESET' TO MOVE ELEMENTS BACK TO THEIR STARTING ORDER if(sortby){ sort('reset', null, $cont, config); }; // RE-HIDE ALL ELEMENTS TEMPORARILY SHOWN $toshow.hide(); // SET DISPLAY PROPERTY OF PRE-EXISTING ELEMENTS BACK TO THEIR // ORIGINAL PROPERTY, INCASE WE ARE CHANGING LAYOUT MODE $pre.css('display',config.origDisplay); // ADD/REMOVE GRID AND LIST CLASSES FROM CONTAINER if(config.origDisplay == 'block'){ $cont.addClass(config.listClass); $toshow.css('display', config.targetDisplayList); } else { $cont.removeClass(config.listClass); $toshow.css('display', config.targetDisplayGrid); }; // IF WE ARE ANIMATING CONTAINER, RESET IT TO ITS STARTING HEIGHT if(config.resizeContainer)$par.css('height', config.origHeight+'px'); // ADD TRANSFORMS TO ALL ELEMENTS TO SHOW var toShowCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; toShowCSS[a+'transform'] = config.scale+' '+config.rotateX+' '+config.rotateY+' '+config.rotateZ; toShowCSS[a+'filter'] = config.blur+' '+config.grayscale; }; $toshow.css(toShowCSS); // FOR EACH PRE-EXISTING ELEMENT, SUBTRACT ITS INTERMEDIATE POSITION FROM ITS ORIGINAL POSITION // TO GET ITS STARTING OFFSET $pre.each(function(){ var data = this.data, $t = $(this); if ($t.hasClass('mix_tohide')){ data.preTX = data.origPos.left - data.hideInterPos.left; data.preTY = data.origPos.top - data.hideInterPos.top; } else { data.preTX = data.origPos.left - data.preInterPos.left; data.preTY = data.origPos.top - data.preInterPos.top; }; var preCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; preCSS[a+'transform'] = 'translate('+data.preTX+'px,'+data.preTY+'px)'; }; $t.css(preCSS); }); // ADD/REMOVE GRID AND LIST CLASSES FROM CONTAINER if(config.layoutMode == 'list'){ $cont.addClass(config.listClass); $cont.removeClass(config.gridClass); } else { $cont.addClass(config.gridClass); $cont.removeClass(config.listClass); }; // WRAP ANIMATION FUNCTIONS IN 10ms TIMEOUT TO PREVENT RACE CONDITION var delay = setTimeout(function(){ // APPLY TRANSITION TIMING TO CONTAINER, AND BEGIN ANIMATION TO NEW HEIGHT if(config.resizeContainer){ var containerCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; containerCSS[a+'transition'] = 'all '+speed+'ms ease-in-out'; containerCSS['height'] = config.newHeight+'px'; }; $par.css(containerCSS); }; // BEGIN FADING IN/OUT OF ALL ELEMENTS TO SHOW/HIDE $tohide.css('opacity',config.fade); $toshow.css('opacity',1); // FOR EACH ELEMENT BEING SHOWN, CALCULATE ITS TRAJECTORY BY SUBTRACTING // ITS INTERMEDIATE POSITION FROM ITS FINAL POSITION. // ALSO ADD SPEED AND EASING $toshow.each(function(){ var data = this.data; data.tX = data.finalPos.left - data.showInterPos.left; data.tY = data.finalPos.top - data.showInterPos.top; var toShowCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; toShowCSS[a+'transition-property'] = a+'transform, '+a+'filter, opacity'; toShowCSS[a+'transition-timing-function'] = config.easing+', linear, linear'; toShowCSS[a+'transition-duration'] = speed+'ms'; toShowCSS[a+'transition-delay'] = '0'; toShowCSS[a+'transform'] = 'translate('+data.tX+'px,'+data.tY+'px)'; toShowCSS[a+'filter'] = 'none'; }; $(this).css('-webkit-transition', 'all '+speed+'ms '+config.easingFallback).css(toShowCSS); }); // FOR EACH PRE-EXISTING ELEMENT, IF IT HAS A FINAL POSITION, CALCULATE // ITS TRAJETORY BY SUBTRACTING ITS INTERMEDIATE POSITION FROM ITS FINAL POSITION. // ALSO ADD SPEED AND EASING $pre.each(function(){ var data = this.data data.tX = data.finalPrePos.left != 0 ? data.finalPrePos.left - data.preInterPos.left : 0; data.tY = data.finalPrePos.left != 0 ? data.finalPrePos.top - data.preInterPos.top : 0; var preCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; preCSS[a+'transition'] = 'all '+speed+'ms '+config.easing; preCSS[a+'transform'] = 'translate('+data.tX+'px,'+data.tY+'px)'; }; $(this).css('-webkit-transition', 'all '+speed+'ms '+config.easingFallback).css(preCSS); }); // BEGIN TRANSFORMS ON ALL ELEMENTS TO BE HIDDEN var toHideCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; toHideCSS[a+'transition'] = 'all '+speed+'ms '+config.easing+', '+a+'filter '+speed+'ms linear, opacity '+speed+'ms linear'; toHideCSS[a+'transform'] = config.scale+' '+config.rotateX+' '+config.rotateY+' '+config.rotateZ; toHideCSS[a+'filter'] = config.blur+' '+config.grayscale; toHideCSS['opacity'] = config.fade; }; $tohide.css(toHideCSS); // ALL ANIMATIONS HAVE NOW BEEN STARTED, NOW LISTEN FOR TRANSITION END: $par.bind('webkitTransitionEnd transitionend otransitionend oTransitionEnd',function(e){ if (e.originalEvent.propertyName.indexOf('transform') > -1 || e.originalEvent.propertyName.indexOf('opacity') > -1){ if(mixSelector.indexOf('.') > -1){ // IF MIXSELECTOR IS A CLASS NAME if($(e.target).hasClass(mixSelector.replace('.',''))){ resetFilter(); }; } else { // IF MIXSELECTOR IS A TAG if($(e.target).is(mixSelector)){ resetFilter(); }; }; }; }); },10); // LAST RESORT EMERGENCY FAILSAFE config.failsafe = setTimeout(function(){ if(config.mixing){ resetFilter(); }; }, speed + 400); } else { // ELSE IF NOTHING TO SHOW, AND EVERYTHING TO BE HIDDEN // IF WE ARE RESIZING CONTAINER, SET ITS STARTING HEIGHT if(config.resizeContainer)$par.css('height', config.origHeight+'px'); // IF IE, FUCK OFF, AND THEN GO HOME if(!window.atob){ resetFilter(); return false; }; // GROUP ALL ELEMENTS TO HIDE INTO JQUERY OBJECT $tohide = $hide; // WRAP ANIMATION FUNCTIONS IN A 10ms DELAY TO PREVENT RACE CONDITION var delay = setTimeout(function(){ // APPLY PERSPECTIVE TO CONTAINER $par.css(config.perspective); // APPLY TRANSITION TIMING TO CONTAINER, AND BEGIN ANIMATION TO NEW HEIGHT if(config.resizeContainer){ var containerCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; containerCSS[a+'transition'] = 'height '+speed+'ms ease-in-out'; containerCSS['height'] = config.minHeight+'px'; }; $par.css(containerCSS); }; // APPLY TRANSITION TIMING TO ALL TARGET ELEMENTS $targets.css(config.transition); // GET TOTAL NUMBER OF ELEMENTS TO HIDE var totalHide = $hide.length; // IF SOMETHING TO HIDE: if(totalHide){ // BEGIN TRANSFORMS ON ALL ELEMENTS TO BE HIDDEN var toHideCSS = {}; for(var i = 0; i<2; i++){ var a = i==0 ? a = config.prefix : ''; toHideCSS[a+'transform'] = config.scale+' '+config.rotateX+' '+config.rotateY+' '+config.rotateZ; toHideCSS[a+'filter'] = config.blur+' '+config.grayscale; toHideCSS['opacity'] = config.fade; }; $tohide.css(toHideCSS); // ALL ANIMATIONS HAVE NOW BEEN STARTED, NOW LISTEN FOR TRANSITION END: $par.bind('webkitTransitionEnd transitionend otransitionend oTransitionEnd',function(e){ if (e.originalEvent.propertyName.indexOf('transform') > -1 || e.originalEvent.propertyName.indexOf('opacity') > -1){ $cont.addClass(config.failClass); resetFilter(); }; }); } else { // ELSE, WE'RE DONE MIXING config.mixing = false; }; }, 10); }; // CLEAN UP AND RESET FUNCTION function resetFilter(){ // UNBIND TRANSITION END EVENTS FROM CONTAINER $par.unbind('webkitTransitionEnd transitionend otransitionend oTransitionEnd'); // IF A SORT ARGUMENT HAS BEEN SENT, SORT ELEMENTS TO THEIR FINAL ORDER if(sortby){ sort(sortby, order, $cont, config); }; // EMPTY SORTING ARRAYS config.startOrder = [], config.newOrder = [], config.origSort = [], config.checkSort = []; // REMOVE INLINE STYLES FROM ALL TARGET ELEMENTS AND SLAM THE BRAKES ON $targets.removeStyle( config.prefix+'filter, filter, '+config.prefix+'transform, transform, opacity, display' ).css(config.clean).removeAttr('data-checksum'); // BECAUSE IE SUCKS if(!window.atob){ $targets.css({ display: 'none', opacity: '0' }); }; // REMOVE HEIGHT FROM CONTAINER ONLY IF RESIZING var remH = config.resizeContainer ? 'height' : ''; // REMOVE INLINE STYLES FROM CONTAINER $par.removeStyle( config.prefix+'transition, transition, '+config.prefix+'perspective, perspective, '+config.prefix+'perspective-origin, perspective-origin, '+remH ); // ADD FINAL DISPLAY PROPERTIES AND OPACITY TO ALL SHOWN ELEMENTS // CACHE CURRENT LAYOUT MODE & SORT FOR NEXT MIX if(config.layoutMode == 'list'){ $show.css({display:config.targetDisplayList, opacity:'1'}); config.origDisplay = config.targetDisplayList; } else { $show.css({display:config.targetDisplayGrid, opacity:'1'}); config.origDisplay = config.targetDisplayGrid; }; config.origLayout = config.layoutMode; var wait = setTimeout(function(){ // LET GO OF THE BRAKES $targets.removeStyle(config.prefix+'transition, transition'); // WE'RE DONE MIXING config.mixing = false; // FIRE "ONMIXEND" CALLBACK if(typeof config.onMixEnd == 'function') { var output = config.onMixEnd.call(this, config); // UPDATE CONFIG IF DATA RETURNED config = output ? output : config; }; }); }; }; // SORT FUNCTION function sort(sortby, order, $cont, config){ // COMPARE BY ATTRIBUTE function compare(a,b) { var sortAttrA = isNaN(a.attr(sortby) * 1) ? a.attr(sortby).toLowerCase() : a.attr(sortby) * 1, sortAttrB = isNaN(b.attr(sortby) * 1) ? b.attr(sortby).toLowerCase() : b.attr(sortby) * 1; if (sortAttrA < sortAttrB) return -1; if (sortAttrA > sortAttrB) return 1; return 0; }; // REBUILD DOM function rebuild(element){ if(order == 'asc'){ $sortWrapper.prepend(element).prepend(' '); } else { $sortWrapper.append(element).append(' '); }; }; // RANDOMIZE ARRAY function arrayShuffle(oldArray){ var newArray = oldArray.slice(); var len = newArray.length; var i = len; while (i--){ var p = parseInt(Math.random()*len); var t = newArray[i]; newArray[i] = newArray[p]; newArray[p] = t; }; return newArray; }; // SORT $cont.find(config.targetSelector).wrapAll('
'); var $sortWrapper = $cont.find('.mix_sorter'); if(!config.origSort.length){ $sortWrapper.find(config.targetSelector+':visible').each(function(){ $(this).wrap(''); config.origSort.push($(this).parent().html().replace(/\s+/g, '')); $(this).unwrap(); }); }; $sortWrapper.empty(); if(sortby == 'reset'){ $.each(config.startOrder,function(){ $sortWrapper.append(this).append(' '); }); } else if(sortby == 'default'){ $.each(config.origOrder,function(){ rebuild(this); }); } else if(sortby == 'random'){ if(!config.newOrder.length){ config.newOrder = arrayShuffle(config.startOrder); }; $.each(config.newOrder,function(){ $sortWrapper.append(this).append(' '); }); } else if(sortby == 'custom'){ $.each(order, function(){ rebuild(this); }); } else { // SORT BY ATTRIBUTE if(typeof config.origOrder[0].attr(sortby) === 'undefined'){ console.log('No such attribute found. Terminating'); return false; }; if(!config.newOrder.length){ $.each(config.origOrder,function(){ config.newOrder.push($(this)); }); config.newOrder.sort(compare); }; $.each(config.newOrder,function(){ rebuild(this); }); }; config.checkSort = []; $sortWrapper.find(config.targetSelector+':visible').each(function(i){ var $t = $(this); if(i == 0){ // PREVENT COMPARE RETURNING FALSE POSITIVES ON ELEMENTS WITH NO CLASS/ATTRIBUTES $t.attr('data-checksum','1'); }; $t.wrap(''); config.checkSort.push($t.parent().html().replace(/\s+/g, '')); $t.unwrap(); }); $cont.find(config.targetSelector).unwrap(); }; // FIND VENDOR PREFIX function prefix(el) { var prefixes = ["Webkit", "Moz", "O", "ms"]; for (var i = 0; i < prefixes.length; i++){ if (prefixes[i] + "Transition" in el.style){ return prefixes[i]; }; }; return "transition" in el.style ? "" : false; }; // REMOVE SPECIFIC STYLES $.fn.removeStyle = function(style){ return this.each(function(){ var obj = $(this); style = style.replace(/\s+/g, ''); var styles = style.split(','); $.each(styles,function(){ var search = new RegExp(this.toString() + '[^;]+;?', 'g'); obj.attr('style', function(i, style){ if(style) return style.replace(search, ''); }); }); }); }; // COMPARE ARRAYS function compareArr(a,b){ if (a.length != b.length) return false; for (var i = 0; i < b.length; i++){ if (a[i].compare) { if (!a[i].compare(b[i])) return false; }; if (a[i] !== b[i]) return false; }; return true; }; // BUILD FILTER ARRAY(S) function buildFilterArray(str){ // CLEAN FILTER STRING str = str.replace(/\s{2,}/g, ' '); // FOR EACH PEROID SEPERATED CLASS NAME, ADD STRING TO FILTER ARRAY var arr = str.split(' '); // IF ALL, REPLACE WITH MIX_ALL $.each(arr,function(i){ if(this == 'all')arr[i] = 'mix_all'; }); if(arr[0] == "")arr.shift(); return arr; }; })(jQuery);