mixitup.js (52224B)
1 /**! 2 * MixItUp v2.1.10 3 * 4 * @copyright Copyright 2015 KunkaLabs Limited. 5 * @author KunkaLabs Limited. 6 * @link https://mixitup.kunkalabs.com 7 * 8 * @license Commercial use requires a commercial license. 9 * https://mixitup.kunkalabs.com/licenses/ 10 * 11 * Non-commercial use permitted under terms of CC-BY-NC license. 12 * http://creativecommons.org/licenses/by-nc/3.0/ 13 */ 14 15 (function($, undf){ 16 'use strict'; 17 18 /** 19 * MixItUp Constructor Function 20 * @constructor 21 * @extends jQuery 22 */ 23 24 $.MixItUp = function(){ 25 var self = this; 26 27 self._execAction('_constructor', 0); 28 29 $.extend(self, { 30 31 /* Public Properties 32 ---------------------------------------------------------------------- */ 33 34 selectors: { 35 target: '.mix', 36 filter: '.filter', 37 sort: '.sort' 38 }, 39 40 animation: { 41 enable: true, 42 effects: 'fade scale', 43 duration: 600, 44 easing: 'ease', 45 perspectiveDistance: '3000', 46 perspectiveOrigin: '50% 50%', 47 queue: true, 48 queueLimit: 1, 49 animateChangeLayout: false, 50 animateResizeContainer: true, 51 animateResizeTargets: false, 52 staggerSequence: false, 53 reverseOut: false 54 }, 55 56 callbacks: { 57 onMixLoad: false, 58 onMixStart: false, 59 onMixBusy: false, 60 onMixEnd: false, 61 onMixFail: false, 62 _user: false 63 }, 64 65 controls: { 66 enable: true, 67 live: false, 68 toggleFilterButtons: false, 69 toggleLogic: 'or', 70 activeClass: 'active' 71 }, 72 73 layout: { 74 display: 'inline-block', 75 containerClass: '', 76 containerClassFail: 'fail' 77 }, 78 79 load: { 80 filter: 'all', 81 sort: false 82 }, 83 84 /* Private Properties 85 ---------------------------------------------------------------------- */ 86 87 _$body: null, 88 _$container: null, 89 _$targets: null, 90 _$parent: null, 91 _$sortButtons: null, 92 _$filterButtons: null, 93 94 _suckMode: false, 95 _mixing: false, 96 _sorting: false, 97 _clicking: false, 98 _loading: true, 99 _changingLayout: false, 100 _changingClass: false, 101 _changingDisplay: false, 102 103 _origOrder: [], 104 _startOrder: [], 105 _newOrder: [], 106 _activeFilter: null, 107 _toggleArray: [], 108 _toggleString: '', 109 _activeSort: 'default:asc', 110 _newSort: null, 111 _startHeight: null, 112 _newHeight: null, 113 _incPadding: true, 114 _newDisplay: null, 115 _newClass: null, 116 _targetsBound: 0, 117 _targetsDone: 0, 118 _queue: [], 119 120 _$show: $(), 121 _$hide: $() 122 }); 123 124 self._execAction('_constructor', 1); 125 }; 126 127 /** 128 * MixItUp Prototype 129 * @override 130 */ 131 132 $.MixItUp.prototype = { 133 constructor: $.MixItUp, 134 135 /* Static Properties 136 ---------------------------------------------------------------------- */ 137 138 _instances: {}, 139 _handled: { 140 _filter: {}, 141 _sort: {} 142 }, 143 _bound: { 144 _filter: {}, 145 _sort: {} 146 }, 147 _actions: {}, 148 _filters: {}, 149 150 /* Static Methods 151 ---------------------------------------------------------------------- */ 152 153 /** 154 * Extend 155 * @since 2.1.0 156 * @param {object} new properties/methods 157 * @extends {object} prototype 158 */ 159 160 extend: function(extension){ 161 for(var key in extension){ 162 $.MixItUp.prototype[key] = extension[key]; 163 } 164 }, 165 166 /** 167 * Add Action 168 * @since 2.1.0 169 * @param {string} hook name 170 * @param {string} namespace 171 * @param {function} function to execute 172 * @param {number} priority 173 * @extends {object} $.MixItUp.prototype._actions 174 */ 175 176 addAction: function(hook, name, func, priority){ 177 $.MixItUp.prototype._addHook('_actions', hook, name, func, priority); 178 }, 179 180 /** 181 * Add Filter 182 * @since 2.1.0 183 * @param {string} hook name 184 * @param {string} namespace 185 * @param {function} function to execute 186 * @param {number} priority 187 * @extends {object} $.MixItUp.prototype._filters 188 */ 189 190 addFilter: function(hook, name, func, priority){ 191 $.MixItUp.prototype._addHook('_filters', hook, name, func, priority); 192 }, 193 194 /** 195 * Add Hook 196 * @since 2.1.0 197 * @param {string} type of hook 198 * @param {string} hook name 199 * @param {function} function to execute 200 * @param {number} priority 201 * @extends {object} $.MixItUp.prototype._filters 202 */ 203 204 _addHook: function(type, hook, name, func, priority){ 205 var collection = $.MixItUp.prototype[type], 206 obj = {}; 207 208 priority = (priority === 1 || priority === 'post') ? 'post' : 'pre'; 209 210 obj[hook] = {}; 211 obj[hook][priority] = {}; 212 obj[hook][priority][name] = func; 213 214 $.extend(true, collection, obj); 215 }, 216 217 218 /* Private Methods 219 ---------------------------------------------------------------------- */ 220 221 /** 222 * Initialise 223 * @since 2.0.0 224 * @param {object} domNode 225 * @param {object} config 226 */ 227 228 _init: function(domNode, config){ 229 var self = this; 230 231 self._execAction('_init', 0, arguments); 232 233 config && $.extend(true, self, config); 234 235 self._$body = $('body'); 236 self._domNode = domNode; 237 self._$container = $(domNode); 238 self._$container.addClass(self.layout.containerClass); 239 self._id = domNode.id; 240 241 self._platformDetect(); 242 243 self._brake = self._getPrefixedCSS('transition', 'none'); 244 245 self._refresh(true); 246 247 self._$parent = self._$targets.parent().length ? self._$targets.parent() : self._$container; 248 249 if(self.load.sort){ 250 self._newSort = self._parseSort(self.load.sort); 251 self._newSortString = self.load.sort; 252 self._activeSort = self.load.sort; 253 self._sort(); 254 self._printSort(); 255 } 256 257 self._activeFilter = self.load.filter === 'all' ? 258 self.selectors.target : 259 self.load.filter === 'none' ? 260 '' : 261 self.load.filter; 262 263 self.controls.enable && self._bindHandlers(); 264 265 if(self.controls.toggleFilterButtons){ 266 self._buildToggleArray(); 267 268 for(var i = 0; i < self._toggleArray.length; i++){ 269 self._updateControls({filter: self._toggleArray[i], sort: self._activeSort}, true); 270 }; 271 } else if(self.controls.enable){ 272 self._updateControls({filter: self._activeFilter, sort: self._activeSort}); 273 } 274 275 self._filter(); 276 277 self._init = true; 278 279 self._$container.data('mixItUp',self); 280 281 self._execAction('_init', 1, arguments); 282 283 self._buildState(); 284 285 self._$targets.css(self._brake); 286 287 self._goMix(self.animation.enable); 288 }, 289 290 /** 291 * Platform Detect 292 * @since 2.0.0 293 */ 294 295 _platformDetect: function(){ 296 var self = this, 297 vendorsTrans = ['Webkit', 'Moz', 'O', 'ms'], 298 vendorsRAF = ['webkit', 'moz'], 299 chrome = window.navigator.appVersion.match(/Chrome\/(\d+)\./) || false, 300 ff = typeof InstallTrigger !== 'undefined', 301 prefix = function(el){ 302 for (var i = 0; i < vendorsTrans.length; i++){ 303 if (vendorsTrans[i] + 'Transition' in el.style){ 304 return { 305 prefix: '-'+vendorsTrans[i].toLowerCase()+'-', 306 vendor: vendorsTrans[i] 307 }; 308 }; 309 }; 310 return 'transition' in el.style ? '' : false; 311 }, 312 transPrefix = prefix(self._domNode); 313 314 self._execAction('_platformDetect', 0); 315 316 self._chrome = chrome ? parseInt(chrome[1], 10) : false; 317 self._ff = ff ? parseInt(window.navigator.userAgent.match(/rv:([^)]+)\)/)[1]) : false; 318 self._prefix = transPrefix.prefix; 319 self._vendor = transPrefix.vendor; 320 self._suckMode = window.atob && self._prefix ? false : true; 321 322 self._suckMode && (self.animation.enable = false); 323 (self._ff && self._ff <= 4) && (self.animation.enable = false); 324 325 /* Polyfills 326 ---------------------------------------------------------------------- */ 327 328 /** 329 * window.requestAnimationFrame 330 */ 331 332 for(var x = 0; x < vendorsRAF.length && !window.requestAnimationFrame; x++){ 333 window.requestAnimationFrame = window[vendorsRAF[x]+'RequestAnimationFrame']; 334 } 335 336 /** 337 * Object.getPrototypeOf 338 */ 339 340 if(typeof Object.getPrototypeOf !== 'function'){ 341 if(typeof 'test'.__proto__ === 'object'){ 342 Object.getPrototypeOf = function(object){ 343 return object.__proto__; 344 }; 345 } else { 346 Object.getPrototypeOf = function(object){ 347 return object.constructor.prototype; 348 }; 349 } 350 } 351 352 /** 353 * Element.nextElementSibling 354 */ 355 356 if(self._domNode.nextElementSibling === undf){ 357 Object.defineProperty(Element.prototype, 'nextElementSibling',{ 358 get: function(){ 359 var el = this.nextSibling; 360 361 while(el){ 362 if(el.nodeType ===1){ 363 return el; 364 } 365 el = el.nextSibling; 366 } 367 return null; 368 } 369 }); 370 } 371 372 self._execAction('_platformDetect', 1); 373 }, 374 375 /** 376 * Refresh 377 * @since 2.0.0 378 * @param {boolean} init 379 * @param {boolean} force 380 */ 381 382 _refresh: function(init, force){ 383 var self = this; 384 385 self._execAction('_refresh', 0, arguments); 386 387 self._$targets = self._$container.find(self.selectors.target); 388 389 for(var i = 0; i < self._$targets.length; i++){ 390 var target = self._$targets[i]; 391 392 if(target.dataset === undf || force){ 393 394 target.dataset = {}; 395 396 for(var j = 0; j < target.attributes.length; j++){ 397 398 var attr = target.attributes[j], 399 name = attr.name, 400 val = attr.value; 401 402 if(name.indexOf('data-') > -1){ 403 var dataName = self._helpers._camelCase(name.substring(5,name.length)); 404 target.dataset[dataName] = val; 405 } 406 } 407 } 408 409 if(target.mixParent === undf){ 410 target.mixParent = self._id; 411 } 412 } 413 414 if( 415 (self._$targets.length && init) || 416 (!self._origOrder.length && self._$targets.length) 417 ){ 418 self._origOrder = []; 419 420 for(var i = 0; i < self._$targets.length; i++){ 421 var target = self._$targets[i]; 422 423 self._origOrder.push(target); 424 } 425 } 426 427 self._execAction('_refresh', 1, arguments); 428 }, 429 430 /** 431 * Bind Handlers 432 * @since 2.0.0 433 */ 434 435 _bindHandlers: function(){ 436 var self = this, 437 filters = $.MixItUp.prototype._bound._filter, 438 sorts = $.MixItUp.prototype._bound._sort; 439 440 self._execAction('_bindHandlers', 0); 441 442 if(self.controls.live){ 443 self._$body 444 .on('click.mixItUp.'+self._id, self.selectors.sort, function(){ 445 self._processClick($(this), 'sort'); 446 }) 447 .on('click.mixItUp.'+self._id, self.selectors.filter, function(){ 448 self._processClick($(this), 'filter'); 449 }); 450 } else { 451 self._$sortButtons = $(self.selectors.sort); 452 self._$filterButtons = $(self.selectors.filter); 453 454 self._$sortButtons.on('click.mixItUp.'+self._id, function(){ 455 self._processClick($(this), 'sort'); 456 }); 457 458 self._$filterButtons.on('click.mixItUp.'+self._id, function(){ 459 self._processClick($(this), 'filter'); 460 }); 461 } 462 463 filters[self.selectors.filter] = (filters[self.selectors.filter] === undf) ? 1 : filters[self.selectors.filter] + 1; 464 sorts[self.selectors.sort] = (sorts[self.selectors.sort] === undf) ? 1 : sorts[self.selectors.sort] + 1; 465 466 self._execAction('_bindHandlers', 1); 467 }, 468 469 /** 470 * Process Click 471 * @since 2.0.0 472 * @param {object} $button 473 * @param {string} type 474 */ 475 476 _processClick: function($button, type){ 477 var self = this, 478 trackClick = function($button, type, off){ 479 var proto = $.MixItUp.prototype; 480 481 proto._handled['_'+type][self.selectors[type]] = (proto._handled['_'+type][self.selectors[type]] === undf) ? 482 1 : 483 proto._handled['_'+type][self.selectors[type]] + 1; 484 485 if(proto._handled['_'+type][self.selectors[type]] === proto._bound['_'+type][self.selectors[type]]){ 486 $button[(off ? 'remove' : 'add')+'Class'](self.controls.activeClass); 487 delete proto._handled['_'+type][self.selectors[type]]; 488 } 489 }; 490 491 self._execAction('_processClick', 0, arguments); 492 493 if(!self._mixing || (self.animation.queue && self._queue.length < self.animation.queueLimit)){ 494 self._clicking = true; 495 496 if(type === 'sort'){ 497 var sort = $button.attr('data-sort'); 498 499 if(!$button.hasClass(self.controls.activeClass) || sort.indexOf('random') > -1){ 500 $(self.selectors.sort).removeClass(self.controls.activeClass); 501 trackClick($button, type); 502 self.sort(sort); 503 } 504 } 505 506 if(type === 'filter') { 507 var filter = $button.attr('data-filter'), 508 ndx, 509 seperator = self.controls.toggleLogic === 'or' ? ',' : ''; 510 511 if(!self.controls.toggleFilterButtons){ 512 if(!$button.hasClass(self.controls.activeClass)){ 513 $(self.selectors.filter).removeClass(self.controls.activeClass); 514 trackClick($button, type); 515 self.filter(filter); 516 } 517 } else { 518 self._buildToggleArray(); 519 520 if(!$button.hasClass(self.controls.activeClass)){ 521 trackClick($button, type); 522 523 self._toggleArray.push(filter); 524 } else { 525 trackClick($button, type, true); 526 ndx = self._toggleArray.indexOf(filter); 527 self._toggleArray.splice(ndx, 1); 528 } 529 530 self._toggleArray = $.grep(self._toggleArray,function(n){return(n);}); 531 532 self._toggleString = self._toggleArray.join(seperator); 533 534 self.filter(self._toggleString); 535 } 536 } 537 538 self._execAction('_processClick', 1, arguments); 539 } else { 540 if(typeof self.callbacks.onMixBusy === 'function'){ 541 self.callbacks.onMixBusy.call(self._domNode, self._state, self); 542 } 543 self._execAction('_processClickBusy', 1, arguments); 544 } 545 }, 546 547 /** 548 * Build Toggle Array 549 * @since 2.0.0 550 */ 551 552 _buildToggleArray: function(){ 553 var self = this, 554 activeFilter = self._activeFilter.replace(/\s/g, ''); 555 556 self._execAction('_buildToggleArray', 0, arguments); 557 558 if(self.controls.toggleLogic === 'or'){ 559 self._toggleArray = activeFilter.split(','); 560 } else { 561 self._toggleArray = activeFilter.split('.'); 562 563 !self._toggleArray[0] && self._toggleArray.shift(); 564 565 for(var i = 0, filter; filter = self._toggleArray[i]; i++){ 566 self._toggleArray[i] = '.'+filter; 567 } 568 } 569 570 self._execAction('_buildToggleArray', 1, arguments); 571 }, 572 573 /** 574 * Update Controls 575 * @since 2.0.0 576 * @param {object} command 577 * @param {boolean} multi 578 */ 579 580 _updateControls: function(command, multi){ 581 var self = this, 582 output = { 583 filter: command.filter, 584 sort: command.sort 585 }, 586 update = function($el, filter){ 587 try { 588 (multi && type === 'filter' && !(output.filter === 'none' || output.filter === '')) ? 589 $el.filter(filter).addClass(self.controls.activeClass) : 590 $el.removeClass(self.controls.activeClass).filter(filter).addClass(self.controls.activeClass); 591 } catch(e) {} 592 }, 593 type = 'filter', 594 $el = null; 595 596 self._execAction('_updateControls', 0, arguments); 597 598 (command.filter === undf) && (output.filter = self._activeFilter); 599 (command.sort === undf) && (output.sort = self._activeSort); 600 (output.filter === self.selectors.target) && (output.filter = 'all'); 601 602 for(var i = 0; i < 2; i++){ 603 $el = self.controls.live ? $(self.selectors[type]) : self['_$'+type+'Buttons']; 604 $el && update($el, '[data-'+type+'="'+output[type]+'"]'); 605 type = 'sort'; 606 } 607 608 self._execAction('_updateControls', 1, arguments); 609 }, 610 611 /** 612 * Filter (private) 613 * @since 2.0.0 614 */ 615 616 _filter: function(){ 617 var self = this; 618 619 self._execAction('_filter', 0); 620 621 for(var i = 0; i < self._$targets.length; i++){ 622 var $target = $(self._$targets[i]); 623 624 if($target.is(self._activeFilter)){ 625 self._$show = self._$show.add($target); 626 } else { 627 self._$hide = self._$hide.add($target); 628 } 629 } 630 631 self._execAction('_filter', 1); 632 }, 633 634 /** 635 * Sort (private) 636 * @since 2.0.0 637 */ 638 639 _sort: function(){ 640 var self = this, 641 arrayShuffle = function(oldArray){ 642 var newArray = oldArray.slice(), 643 len = newArray.length, 644 i = len; 645 646 while(i--){ 647 var p = parseInt(Math.random()*len); 648 var t = newArray[i]; 649 newArray[i] = newArray[p]; 650 newArray[p] = t; 651 }; 652 return newArray; 653 }; 654 655 self._execAction('_sort', 0); 656 657 self._startOrder = []; 658 659 for(var i = 0; i < self._$targets.length; i++){ 660 var target = self._$targets[i]; 661 662 self._startOrder.push(target); 663 } 664 665 switch(self._newSort[0].sortBy){ 666 case 'default': 667 self._newOrder = self._origOrder; 668 break; 669 case 'random': 670 self._newOrder = arrayShuffle(self._startOrder); 671 break; 672 case 'custom': 673 self._newOrder = self._newSort[0].order; 674 break; 675 default: 676 self._newOrder = self._startOrder.concat().sort(function(a, b){ 677 return self._compare(a, b); 678 }); 679 } 680 681 self._execAction('_sort', 1); 682 }, 683 684 /** 685 * Compare Algorithm 686 * @since 2.0.0 687 * @param {string|number} a 688 * @param {string|number} b 689 * @param {number} depth (recursion) 690 * @return {number} 691 */ 692 693 _compare: function(a, b, depth){ 694 depth = depth ? depth : 0; 695 696 var self = this, 697 order = self._newSort[depth].order, 698 getData = function(el){ 699 return el.dataset[self._newSort[depth].sortBy] || 0; 700 }, 701 attrA = isNaN(getData(a) * 1) ? getData(a).toLowerCase() : getData(a) * 1, 702 attrB = isNaN(getData(b) * 1) ? getData(b).toLowerCase() : getData(b) * 1; 703 704 if(attrA < attrB) 705 return order === 'asc' ? -1 : 1; 706 if(attrA > attrB) 707 return order === 'asc' ? 1 : -1; 708 if(attrA === attrB && self._newSort.length > depth+1) 709 return self._compare(a, b, depth+1); 710 711 return 0; 712 }, 713 714 /** 715 * Print Sort 716 * @since 2.0.0 717 * @param {boolean} reset 718 */ 719 720 _printSort: function(reset){ 721 var self = this, 722 order = reset ? self._startOrder : self._newOrder, 723 targets = self._$parent[0].querySelectorAll(self.selectors.target), 724 nextSibling = targets.length ? targets[targets.length -1].nextElementSibling : null, 725 frag = document.createDocumentFragment(); 726 727 self._execAction('_printSort', 0, arguments); 728 729 for(var i = 0; i < targets.length; i++){ 730 var target = targets[i], 731 whiteSpace = target.nextSibling; 732 733 if(target.style.position === 'absolute') continue; 734 735 if(whiteSpace && whiteSpace.nodeName === '#text'){ 736 self._$parent[0].removeChild(whiteSpace); 737 } 738 739 self._$parent[0].removeChild(target); 740 } 741 742 for(var i = 0; i < order.length; i++){ 743 var el = order[i]; 744 745 if(self._newSort[0].sortBy === 'default' && self._newSort[0].order === 'desc' && !reset){ 746 var firstChild = frag.firstChild; 747 frag.insertBefore(el, firstChild); 748 frag.insertBefore(document.createTextNode(' '), el); 749 } else { 750 frag.appendChild(el); 751 frag.appendChild(document.createTextNode(' ')); 752 } 753 } 754 755 nextSibling ? 756 self._$parent[0].insertBefore(frag, nextSibling) : 757 self._$parent[0].appendChild(frag); 758 759 self._execAction('_printSort', 1, arguments); 760 }, 761 762 /** 763 * Parse Sort 764 * @since 2.0.0 765 * @param {string} sortString 766 * @return {array} newSort 767 */ 768 769 _parseSort: function(sortString){ 770 var self = this, 771 rules = typeof sortString === 'string' ? sortString.split(' ') : [sortString], 772 newSort = []; 773 774 for(var i = 0; i < rules.length; i++){ 775 var rule = typeof sortString === 'string' ? rules[i].split(':') : ['custom', rules[i]], 776 ruleObj = { 777 sortBy: self._helpers._camelCase(rule[0]), 778 order: rule[1] || 'asc' 779 }; 780 781 newSort.push(ruleObj); 782 783 if(ruleObj.sortBy === 'default' || ruleObj.sortBy === 'random') break; 784 } 785 786 return self._execFilter('_parseSort', newSort, arguments); 787 }, 788 789 /** 790 * Parse Effects 791 * @since 2.0.0 792 * @return {object} effects 793 */ 794 795 _parseEffects: function(){ 796 var self = this, 797 effects = { 798 opacity: '', 799 transformIn: '', 800 transformOut: '', 801 filter: '' 802 }, 803 parse = function(effect, extract, reverse){ 804 if(self.animation.effects.indexOf(effect) > -1){ 805 if(extract){ 806 var propIndex = self.animation.effects.indexOf(effect+'('); 807 if(propIndex > -1){ 808 var str = self.animation.effects.substring(propIndex), 809 match = /\(([^)]+)\)/.exec(str), 810 val = match[1]; 811 812 return {val: val}; 813 } 814 } 815 return true; 816 } else { 817 return false; 818 } 819 }, 820 negate = function(value, invert){ 821 if(invert){ 822 return value.charAt(0) === '-' ? value.substr(1, value.length) : '-'+value; 823 } else { 824 return value; 825 } 826 }, 827 buildTransform = function(key, invert){ 828 var transforms = [ 829 ['scale', '.01'], 830 ['translateX', '20px'], 831 ['translateY', '20px'], 832 ['translateZ', '20px'], 833 ['rotateX', '90deg'], 834 ['rotateY', '90deg'], 835 ['rotateZ', '180deg'], 836 ]; 837 838 for(var i = 0; i < transforms.length; i++){ 839 var prop = transforms[i][0], 840 def = transforms[i][1], 841 inverted = invert && prop !== 'scale'; 842 843 effects[key] += parse(prop) ? prop+'('+negate(parse(prop, true).val || def, inverted)+') ' : ''; 844 } 845 }; 846 847 effects.opacity = parse('fade') ? parse('fade',true).val || '0' : '1'; 848 849 buildTransform('transformIn'); 850 851 self.animation.reverseOut ? buildTransform('transformOut', true) : (effects.transformOut = effects.transformIn); 852 853 effects.transition = {}; 854 855 effects.transition = self._getPrefixedCSS('transition','all '+self.animation.duration+'ms '+self.animation.easing+', opacity '+self.animation.duration+'ms linear'); 856 857 self.animation.stagger = parse('stagger') ? true : false; 858 self.animation.staggerDuration = parseInt(parse('stagger') ? (parse('stagger',true).val ? parse('stagger',true).val : 100) : 100); 859 860 return self._execFilter('_parseEffects', effects); 861 }, 862 863 /** 864 * Build State 865 * @since 2.0.0 866 * @param {boolean} future 867 * @return {object} futureState 868 */ 869 870 _buildState: function(future){ 871 var self = this, 872 state = {}; 873 874 self._execAction('_buildState', 0); 875 876 state = { 877 activeFilter: self._activeFilter === '' ? 'none' : self._activeFilter, 878 activeSort: future && self._newSortString ? self._newSortString : self._activeSort, 879 fail: !self._$show.length && self._activeFilter !== '', 880 $targets: self._$targets, 881 $show: self._$show, 882 $hide: self._$hide, 883 totalTargets: self._$targets.length, 884 totalShow: self._$show.length, 885 totalHide: self._$hide.length, 886 display: future && self._newDisplay ? self._newDisplay : self.layout.display 887 }; 888 889 if(future){ 890 return self._execFilter('_buildState', state); 891 } else { 892 self._state = state; 893 894 self._execAction('_buildState', 1); 895 } 896 }, 897 898 /** 899 * Go Mix 900 * @since 2.0.0 901 * @param {boolean} animate 902 */ 903 904 _goMix: function(animate){ 905 var self = this, 906 phase1 = function(){ 907 if(self._chrome && (self._chrome === 31)){ 908 chromeFix(self._$parent[0]); 909 } 910 911 self._setInter(); 912 913 phase2(); 914 }, 915 phase2 = function(){ 916 var scrollTop = window.pageYOffset, 917 scrollLeft = window.pageXOffset, 918 docHeight = document.documentElement.scrollHeight; 919 920 self._getInterMixData(); 921 922 self._setFinal(); 923 924 self._getFinalMixData(); 925 926 (window.pageYOffset !== scrollTop) && window.scrollTo(scrollLeft, scrollTop); 927 928 self._prepTargets(); 929 930 if(window.requestAnimationFrame){ 931 requestAnimationFrame(phase3); 932 } else { 933 setTimeout(function(){ 934 phase3(); 935 },20); 936 } 937 }, 938 phase3 = function(){ 939 self._animateTargets(); 940 941 if(self._targetsBound === 0){ 942 self._cleanUp(); 943 } 944 }, 945 chromeFix = function(grid){ 946 var parent = grid.parentElement, 947 placeholder = document.createElement('div'), 948 frag = document.createDocumentFragment(); 949 950 parent.insertBefore(placeholder, grid); 951 frag.appendChild(grid); 952 parent.replaceChild(grid, placeholder); 953 }, 954 futureState = self._buildState(true); 955 956 self._execAction('_goMix', 0, arguments); 957 958 !self.animation.duration && (animate = false); 959 960 self._mixing = true; 961 962 self._$container.removeClass(self.layout.containerClassFail); 963 964 if(typeof self.callbacks.onMixStart === 'function'){ 965 self.callbacks.onMixStart.call(self._domNode, self._state, futureState, self); 966 } 967 968 self._$container.trigger('mixStart', [self._state, futureState, self]); 969 970 self._getOrigMixData(); 971 972 if(animate && !self._suckMode){ 973 974 window.requestAnimationFrame ? 975 requestAnimationFrame(phase1) : 976 phase1(); 977 978 } else { 979 self._cleanUp(); 980 } 981 982 self._execAction('_goMix', 1, arguments); 983 }, 984 985 /** 986 * Get Target Data 987 * @since 2.0.0 988 */ 989 990 _getTargetData: function(el, stage){ 991 var self = this, 992 elStyle; 993 994 el.dataset[stage+'PosX'] = el.offsetLeft; 995 el.dataset[stage+'PosY'] = el.offsetTop; 996 997 if(self.animation.animateResizeTargets){ 998 elStyle = !self._suckMode ? 999 window.getComputedStyle(el) : 1000 { 1001 marginBottom: '', 1002 marginRight: '' 1003 }; 1004 1005 el.dataset[stage+'MarginBottom'] = parseInt(elStyle.marginBottom); 1006 el.dataset[stage+'MarginRight'] = parseInt(elStyle.marginRight); 1007 el.dataset[stage+'Width'] = el.offsetWidth; 1008 el.dataset[stage+'Height'] = el.offsetHeight; 1009 } 1010 }, 1011 1012 /** 1013 * Get Original Mix Data 1014 * @since 2.0.0 1015 */ 1016 1017 _getOrigMixData: function(){ 1018 var self = this, 1019 parentStyle = !self._suckMode ? window.getComputedStyle(self._$parent[0]) : {boxSizing: ''}, 1020 parentBS = parentStyle.boxSizing || parentStyle[self._vendor+'BoxSizing']; 1021 1022 self._incPadding = (parentBS === 'border-box'); 1023 1024 self._execAction('_getOrigMixData', 0); 1025 1026 !self._suckMode && (self.effects = self._parseEffects()); 1027 1028 self._$toHide = self._$hide.filter(':visible'); 1029 self._$toShow = self._$show.filter(':hidden'); 1030 self._$pre = self._$targets.filter(':visible'); 1031 1032 self._startHeight = self._incPadding ? 1033 self._$parent.outerHeight() : 1034 self._$parent.height(); 1035 1036 for(var i = 0; i < self._$pre.length; i++){ 1037 var el = self._$pre[i]; 1038 1039 self._getTargetData(el, 'orig'); 1040 } 1041 1042 self._execAction('_getOrigMixData', 1); 1043 }, 1044 1045 /** 1046 * Set Intermediate Positions 1047 * @since 2.0.0 1048 */ 1049 1050 _setInter: function(){ 1051 var self = this; 1052 1053 self._execAction('_setInter', 0); 1054 1055 if(self._changingLayout && self.animation.animateChangeLayout){ 1056 self._$toShow.css('display',self._newDisplay); 1057 1058 if(self._changingClass){ 1059 self._$container 1060 .removeClass(self.layout.containerClass) 1061 .addClass(self._newClass); 1062 } 1063 } else { 1064 self._$toShow.css('display', self.layout.display); 1065 } 1066 1067 self._execAction('_setInter', 1); 1068 }, 1069 1070 /** 1071 * Get Intermediate Mix Data 1072 * @since 2.0.0 1073 */ 1074 1075 _getInterMixData: function(){ 1076 var self = this; 1077 1078 self._execAction('_getInterMixData', 0); 1079 1080 for(var i = 0; i < self._$toShow.length; i++){ 1081 var el = self._$toShow[i]; 1082 1083 self._getTargetData(el, 'inter'); 1084 } 1085 1086 for(var i = 0; i < self._$pre.length; i++){ 1087 var el = self._$pre[i]; 1088 1089 self._getTargetData(el, 'inter'); 1090 } 1091 1092 self._execAction('_getInterMixData', 1); 1093 }, 1094 1095 /** 1096 * Set Final Positions 1097 * @since 2.0.0 1098 */ 1099 1100 _setFinal: function(){ 1101 var self = this; 1102 1103 self._execAction('_setFinal', 0); 1104 1105 self._sorting && self._printSort(); 1106 1107 self._$toHide.removeStyle('display'); 1108 1109 if(self._changingLayout && self.animation.animateChangeLayout){ 1110 self._$pre.css('display',self._newDisplay); 1111 } 1112 1113 self._execAction('_setFinal', 1); 1114 }, 1115 1116 /** 1117 * Get Final Mix Data 1118 * @since 2.0.0 1119 */ 1120 1121 _getFinalMixData: function(){ 1122 var self = this; 1123 1124 self._execAction('_getFinalMixData', 0); 1125 1126 for(var i = 0; i < self._$toShow.length; i++){ 1127 var el = self._$toShow[i]; 1128 1129 self._getTargetData(el, 'final'); 1130 } 1131 1132 for(var i = 0; i < self._$pre.length; i++){ 1133 var el = self._$pre[i]; 1134 1135 self._getTargetData(el, 'final'); 1136 } 1137 1138 self._newHeight = self._incPadding ? 1139 self._$parent.outerHeight() : 1140 self._$parent.height(); 1141 1142 self._sorting && self._printSort(true); 1143 1144 self._$toShow.removeStyle('display'); 1145 1146 self._$pre.css('display',self.layout.display); 1147 1148 if(self._changingClass && self.animation.animateChangeLayout){ 1149 self._$container 1150 .removeClass(self._newClass) 1151 .addClass(self.layout.containerClass); 1152 } 1153 1154 self._execAction('_getFinalMixData', 1); 1155 }, 1156 1157 /** 1158 * Prepare Targets 1159 * @since 2.0.0 1160 */ 1161 1162 _prepTargets: function(){ 1163 var self = this, 1164 transformCSS = { 1165 _in: self._getPrefixedCSS('transform', self.effects.transformIn), 1166 _out: self._getPrefixedCSS('transform', self.effects.transformOut) 1167 }; 1168 1169 self._execAction('_prepTargets', 0); 1170 1171 if(self.animation.animateResizeContainer){ 1172 self._$parent.css('height',self._startHeight+'px'); 1173 } 1174 1175 for(var i = 0; i < self._$toShow.length; i++){ 1176 var el = self._$toShow[i], 1177 $el = $(el); 1178 1179 el.style.opacity = self.effects.opacity; 1180 el.style.display = (self._changingLayout && self.animation.animateChangeLayout) ? 1181 self._newDisplay : 1182 self.layout.display; 1183 1184 $el.css(transformCSS._in); 1185 1186 if(self.animation.animateResizeTargets){ 1187 el.style.width = el.dataset.finalWidth+'px'; 1188 el.style.height = el.dataset.finalHeight+'px'; 1189 el.style.marginRight = -(el.dataset.finalWidth - el.dataset.interWidth) + (el.dataset.finalMarginRight * 1)+'px'; 1190 el.style.marginBottom = -(el.dataset.finalHeight - el.dataset.interHeight) + (el.dataset.finalMarginBottom * 1)+'px'; 1191 } 1192 } 1193 1194 for(var i = 0; i < self._$pre.length; i++){ 1195 var el = self._$pre[i], 1196 $el = $(el), 1197 translate = { 1198 x: el.dataset.origPosX - el.dataset.interPosX, 1199 y: el.dataset.origPosY - el.dataset.interPosY 1200 }, 1201 transformCSS = self._getPrefixedCSS('transform','translate('+translate.x+'px,'+translate.y+'px)'); 1202 1203 $el.css(transformCSS); 1204 1205 if(self.animation.animateResizeTargets){ 1206 el.style.width = el.dataset.origWidth+'px'; 1207 el.style.height = el.dataset.origHeight+'px'; 1208 1209 if(el.dataset.origWidth - el.dataset.finalWidth){ 1210 el.style.marginRight = -(el.dataset.origWidth - el.dataset.interWidth) + (el.dataset.origMarginRight * 1)+'px'; 1211 } 1212 1213 if(el.dataset.origHeight - el.dataset.finalHeight){ 1214 el.style.marginBottom = -(el.dataset.origHeight - el.dataset.interHeight) + (el.dataset.origMarginBottom * 1) +'px'; 1215 } 1216 } 1217 } 1218 1219 self._execAction('_prepTargets', 1); 1220 }, 1221 1222 /** 1223 * Animate Targets 1224 * @since 2.0.0 1225 */ 1226 1227 _animateTargets: function(){ 1228 var self = this; 1229 1230 self._execAction('_animateTargets', 0); 1231 1232 self._targetsDone = 0; 1233 self._targetsBound = 0; 1234 1235 self._$parent 1236 .css(self._getPrefixedCSS('perspective', self.animation.perspectiveDistance+'px')) 1237 .css(self._getPrefixedCSS('perspective-origin', self.animation.perspectiveOrigin)); 1238 1239 if(self.animation.animateResizeContainer){ 1240 self._$parent 1241 .css(self._getPrefixedCSS('transition','height '+self.animation.duration+'ms ease')) 1242 .css('height',self._newHeight+'px'); 1243 } 1244 1245 for(var i = 0; i < self._$toShow.length; i++){ 1246 var el = self._$toShow[i], 1247 $el = $(el), 1248 translate = { 1249 x: el.dataset.finalPosX - el.dataset.interPosX, 1250 y: el.dataset.finalPosY - el.dataset.interPosY 1251 }, 1252 delay = self._getDelay(i), 1253 toShowCSS = {}; 1254 1255 el.style.opacity = ''; 1256 1257 for(var j = 0; j < 2; j++){ 1258 var a = j === 0 ? a = self._prefix : ''; 1259 1260 if(self._ff && self._ff <= 20){ 1261 toShowCSS[a+'transition-property'] = 'all'; 1262 toShowCSS[a+'transition-timing-function'] = self.animation.easing+'ms'; 1263 toShowCSS[a+'transition-duration'] = self.animation.duration+'ms'; 1264 } 1265 1266 toShowCSS[a+'transition-delay'] = delay+'ms'; 1267 toShowCSS[a+'transform'] = 'translate('+translate.x+'px,'+translate.y+'px)'; 1268 } 1269 1270 if(self.effects.transform || self.effects.opacity){ 1271 self._bindTargetDone($el); 1272 } 1273 1274 (self._ff && self._ff <= 20) ? 1275 $el.css(toShowCSS) : 1276 $el.css(self.effects.transition).css(toShowCSS); 1277 } 1278 1279 for(var i = 0; i < self._$pre.length; i++){ 1280 var el = self._$pre[i], 1281 $el = $(el), 1282 translate = { 1283 x: el.dataset.finalPosX - el.dataset.interPosX, 1284 y: el.dataset.finalPosY - el.dataset.interPosY 1285 }, 1286 delay = self._getDelay(i); 1287 1288 if(!( 1289 el.dataset.finalPosX === el.dataset.origPosX && 1290 el.dataset.finalPosY === el.dataset.origPosY 1291 )){ 1292 self._bindTargetDone($el); 1293 } 1294 1295 $el.css(self._getPrefixedCSS('transition', 'all '+self.animation.duration+'ms '+self.animation.easing+' '+delay+'ms')); 1296 $el.css(self._getPrefixedCSS('transform', 'translate('+translate.x+'px,'+translate.y+'px)')); 1297 1298 if(self.animation.animateResizeTargets){ 1299 if(el.dataset.origWidth - el.dataset.finalWidth && el.dataset.finalWidth * 1){ 1300 el.style.width = el.dataset.finalWidth+'px'; 1301 el.style.marginRight = -(el.dataset.finalWidth - el.dataset.interWidth)+(el.dataset.finalMarginRight * 1)+'px'; 1302 } 1303 1304 if(el.dataset.origHeight - el.dataset.finalHeight && el.dataset.finalHeight * 1){ 1305 el.style.height = el.dataset.finalHeight+'px'; 1306 el.style.marginBottom = -(el.dataset.finalHeight - el.dataset.interHeight)+(el.dataset.finalMarginBottom * 1) +'px'; 1307 } 1308 } 1309 } 1310 1311 if(self._changingClass){ 1312 self._$container 1313 .removeClass(self.layout.containerClass) 1314 .addClass(self._newClass); 1315 } 1316 1317 for(var i = 0; i < self._$toHide.length; i++){ 1318 var el = self._$toHide[i], 1319 $el = $(el), 1320 delay = self._getDelay(i), 1321 toHideCSS = {}; 1322 1323 for(var j = 0; j<2; j++){ 1324 var a = j === 0 ? a = self._prefix : ''; 1325 1326 toHideCSS[a+'transition-delay'] = delay+'ms'; 1327 toHideCSS[a+'transform'] = self.effects.transformOut; 1328 toHideCSS.opacity = self.effects.opacity; 1329 } 1330 1331 $el.css(self.effects.transition).css(toHideCSS); 1332 1333 if(self.effects.transform || self.effects.opacity){ 1334 self._bindTargetDone($el); 1335 }; 1336 } 1337 1338 self._execAction('_animateTargets', 1); 1339 1340 }, 1341 1342 /** 1343 * Bind Targets TransitionEnd 1344 * @since 2.0.0 1345 * @param {object} $el 1346 */ 1347 1348 _bindTargetDone: function($el){ 1349 var self = this, 1350 el = $el[0]; 1351 1352 self._execAction('_bindTargetDone', 0, arguments); 1353 1354 if(!el.dataset.bound){ 1355 1356 el.dataset.bound = true; 1357 self._targetsBound++; 1358 1359 $el.on('webkitTransitionEnd.mixItUp transitionend.mixItUp',function(e){ 1360 if( 1361 (e.originalEvent.propertyName.indexOf('transform') > -1 || 1362 e.originalEvent.propertyName.indexOf('opacity') > -1) && 1363 $(e.originalEvent.target).is(self.selectors.target) 1364 ){ 1365 $el.off('.mixItUp'); 1366 delete el.dataset.bound; 1367 self._targetDone(); 1368 } 1369 }); 1370 } 1371 1372 self._execAction('_bindTargetDone', 1, arguments); 1373 }, 1374 1375 /** 1376 * Target Done 1377 * @since 2.0.0 1378 */ 1379 1380 _targetDone: function(){ 1381 var self = this; 1382 1383 self._execAction('_targetDone', 0); 1384 1385 self._targetsDone++; 1386 1387 (self._targetsDone === self._targetsBound) && self._cleanUp(); 1388 1389 self._execAction('_targetDone', 1); 1390 }, 1391 1392 /** 1393 * Clean Up 1394 * @since 2.0.0 1395 */ 1396 1397 _cleanUp: function(){ 1398 var self = this, 1399 targetStyles = self.animation.animateResizeTargets ? 1400 'transform opacity width height margin-bottom margin-right' : 1401 'transform opacity', 1402 unBrake = function(){ 1403 self._$targets.removeStyle('transition', self._prefix); 1404 }; 1405 1406 self._execAction('_cleanUp', 0); 1407 1408 !self._changingLayout ? 1409 self._$show.css('display',self.layout.display) : 1410 self._$show.css('display',self._newDisplay); 1411 1412 self._$targets.css(self._brake); 1413 1414 self._$targets 1415 .removeStyle(targetStyles, self._prefix) 1416 .removeAttr('data-inter-pos-x data-inter-pos-y data-final-pos-x data-final-pos-y data-orig-pos-x data-orig-pos-y data-orig-height data-orig-width data-final-height data-final-width data-inter-width data-inter-height data-orig-margin-right data-orig-margin-bottom data-inter-margin-right data-inter-margin-bottom data-final-margin-right data-final-margin-bottom'); 1417 1418 self._$hide.removeStyle('display'); 1419 1420 self._$parent.removeStyle('height transition perspective-distance perspective perspective-origin-x perspective-origin-y perspective-origin perspectiveOrigin', self._prefix); 1421 1422 if(self._sorting){ 1423 self._printSort(); 1424 self._activeSort = self._newSortString; 1425 self._sorting = false; 1426 } 1427 1428 if(self._changingLayout){ 1429 if(self._changingDisplay){ 1430 self.layout.display = self._newDisplay; 1431 self._changingDisplay = false; 1432 } 1433 1434 if(self._changingClass){ 1435 self._$parent.removeClass(self.layout.containerClass).addClass(self._newClass); 1436 self.layout.containerClass = self._newClass; 1437 self._changingClass = false; 1438 } 1439 1440 self._changingLayout = false; 1441 } 1442 1443 self._refresh(); 1444 1445 self._buildState(); 1446 1447 if(self._state.fail){ 1448 self._$container.addClass(self.layout.containerClassFail); 1449 } 1450 1451 self._$show = $(); 1452 self._$hide = $(); 1453 1454 if(window.requestAnimationFrame){ 1455 requestAnimationFrame(unBrake); 1456 } 1457 1458 self._mixing = false; 1459 1460 if(typeof self.callbacks._user === 'function'){ 1461 self.callbacks._user.call(self._domNode, self._state, self); 1462 } 1463 1464 if(typeof self.callbacks.onMixEnd === 'function'){ 1465 self.callbacks.onMixEnd.call(self._domNode, self._state, self); 1466 } 1467 1468 self._$container.trigger('mixEnd', [self._state, self]); 1469 1470 if(self._state.fail){ 1471 (typeof self.callbacks.onMixFail === 'function') && self.callbacks.onMixFail.call(self._domNode, self._state, self); 1472 self._$container.trigger('mixFail', [self._state, self]); 1473 } 1474 1475 if(self._loading){ 1476 (typeof self.callbacks.onMixLoad === 'function') && self.callbacks.onMixLoad.call(self._domNode, self._state, self); 1477 self._$container.trigger('mixLoad', [self._state, self]); 1478 } 1479 1480 if(self._queue.length){ 1481 self._execAction('_queue', 0); 1482 1483 self.multiMix(self._queue[0][0],self._queue[0][1],self._queue[0][2]); 1484 self._queue.splice(0, 1); 1485 } 1486 1487 self._execAction('_cleanUp', 1); 1488 1489 self._loading = false; 1490 }, 1491 1492 /** 1493 * Get Prefixed CSS 1494 * @since 2.0.0 1495 * @param {string} property 1496 * @param {string} value 1497 * @param {boolean} prefixValue 1498 * @return {object} styles 1499 */ 1500 1501 _getPrefixedCSS: function(property, value, prefixValue){ 1502 var self = this, 1503 styles = {}, 1504 prefix = '', 1505 i = -1; 1506 1507 for(i = 0; i < 2; i++){ 1508 prefix = i === 0 ? self._prefix : ''; 1509 prefixValue ? styles[prefix+property] = prefix+value : styles[prefix+property] = value; 1510 } 1511 1512 return self._execFilter('_getPrefixedCSS', styles, arguments); 1513 }, 1514 1515 /** 1516 * Get Delay 1517 * @since 2.0.0 1518 * @param {number} i 1519 * @return {number} delay 1520 */ 1521 1522 _getDelay: function(i){ 1523 var self = this, 1524 n = typeof self.animation.staggerSequence === 'function' ? self.animation.staggerSequence.call(self._domNode, i, self._state) : i, 1525 delay = self.animation.stagger ? n * self.animation.staggerDuration : 0; 1526 1527 return self._execFilter('_getDelay', delay, arguments); 1528 }, 1529 1530 /** 1531 * Parse MultiMix Arguments 1532 * @since 2.0.0 1533 * @param {array} args 1534 * @return {object} output 1535 */ 1536 1537 _parseMultiMixArgs: function(args){ 1538 var self = this, 1539 output = { 1540 command: null, 1541 animate: self.animation.enable, 1542 callback: null 1543 }; 1544 1545 for(var i = 0; i < args.length; i++){ 1546 var arg = args[i]; 1547 1548 if(arg !== null){ 1549 if(typeof arg === 'object' || typeof arg === 'string'){ 1550 output.command = arg; 1551 } else if(typeof arg === 'boolean'){ 1552 output.animate = arg; 1553 } else if(typeof arg === 'function'){ 1554 output.callback = arg; 1555 } 1556 } 1557 } 1558 1559 return self._execFilter('_parseMultiMixArgs', output, arguments); 1560 }, 1561 1562 /** 1563 * Parse Insert Arguments 1564 * @since 2.0.0 1565 * @param {array} args 1566 * @return {object} output 1567 */ 1568 1569 _parseInsertArgs: function(args){ 1570 var self = this, 1571 output = { 1572 index: 0, 1573 $object: $(), 1574 multiMix: {filter: self._state.activeFilter}, 1575 callback: null 1576 }; 1577 1578 for(var i = 0; i < args.length; i++){ 1579 var arg = args[i]; 1580 1581 if(typeof arg === 'number'){ 1582 output.index = arg; 1583 } else if(typeof arg === 'object' && arg instanceof $){ 1584 output.$object = arg; 1585 } else if(typeof arg === 'object' && self._helpers._isElement(arg)){ 1586 output.$object = $(arg); 1587 } else if(typeof arg === 'object' && arg !== null){ 1588 output.multiMix = arg; 1589 } else if(typeof arg === 'boolean' && !arg){ 1590 output.multiMix = false; 1591 } else if(typeof arg === 'function'){ 1592 output.callback = arg; 1593 } 1594 } 1595 1596 return self._execFilter('_parseInsertArgs', output, arguments); 1597 }, 1598 1599 /** 1600 * Execute Action 1601 * @since 2.0.0 1602 * @param {string} methodName 1603 * @param {boolean} isPost 1604 * @param {array} args 1605 */ 1606 1607 _execAction: function(methodName, isPost, args){ 1608 var self = this, 1609 context = isPost ? 'post' : 'pre'; 1610 1611 if(!self._actions.isEmptyObject && self._actions.hasOwnProperty(methodName)){ 1612 for(var key in self._actions[methodName][context]){ 1613 self._actions[methodName][context][key].call(self, args); 1614 } 1615 } 1616 }, 1617 1618 /** 1619 * Execute Filter 1620 * @since 2.0.0 1621 * @param {string} methodName 1622 * @param {mixed} value 1623 * @return {mixed} value 1624 */ 1625 1626 _execFilter: function(methodName, value, args){ 1627 var self = this; 1628 1629 if(!self._filters.isEmptyObject && self._filters.hasOwnProperty(methodName)){ 1630 for(var key in self._filters[methodName]){ 1631 return self._filters[methodName][key].call(self, args); 1632 } 1633 } else { 1634 return value; 1635 } 1636 }, 1637 1638 /* Helpers 1639 ---------------------------------------------------------------------- */ 1640 1641 _helpers: { 1642 1643 /** 1644 * CamelCase 1645 * @since 2.0.0 1646 * @param {string} 1647 * @return {string} 1648 */ 1649 1650 _camelCase: function(string){ 1651 return string.replace(/-([a-z])/g, function(g){ 1652 return g[1].toUpperCase(); 1653 }); 1654 }, 1655 1656 /** 1657 * Is Element 1658 * @since 2.1.3 1659 * @param {object} element to test 1660 * @return {boolean} 1661 */ 1662 1663 _isElement: function(el){ 1664 if(window.HTMLElement){ 1665 return el instanceof HTMLElement; 1666 } else { 1667 return ( 1668 el !== null && 1669 el.nodeType === 1 && 1670 el.nodeName === 'string' 1671 ); 1672 } 1673 } 1674 }, 1675 1676 /* Public Methods 1677 ---------------------------------------------------------------------- */ 1678 1679 /** 1680 * Is Mixing 1681 * @since 2.0.0 1682 * @return {boolean} 1683 */ 1684 1685 isMixing: function(){ 1686 var self = this; 1687 1688 return self._execFilter('isMixing', self._mixing); 1689 }, 1690 1691 /** 1692 * Filter (public) 1693 * @since 2.0.0 1694 * @param {array} arguments 1695 */ 1696 1697 filter: function(){ 1698 var self = this, 1699 args = self._parseMultiMixArgs(arguments); 1700 1701 self._clicking && (self._toggleString = ''); 1702 1703 self.multiMix({filter: args.command}, args.animate, args.callback); 1704 }, 1705 1706 /** 1707 * Sort (public) 1708 * @since 2.0.0 1709 * @param {array} arguments 1710 */ 1711 1712 sort: function(){ 1713 var self = this, 1714 args = self._parseMultiMixArgs(arguments); 1715 1716 self.multiMix({sort: args.command}, args.animate, args.callback); 1717 }, 1718 1719 /** 1720 * Change Layout (public) 1721 * @since 2.0.0 1722 * @param {array} arguments 1723 */ 1724 1725 changeLayout: function(){ 1726 var self = this, 1727 args = self._parseMultiMixArgs(arguments); 1728 1729 self.multiMix({changeLayout: args.command}, args.animate, args.callback); 1730 }, 1731 1732 /** 1733 * MultiMix 1734 * @since 2.0.0 1735 * @param {array} arguments 1736 */ 1737 1738 multiMix: function(){ 1739 var self = this, 1740 args = self._parseMultiMixArgs(arguments); 1741 1742 self._execAction('multiMix', 0, arguments); 1743 1744 if(!self._mixing){ 1745 if(self.controls.enable && !self._clicking){ 1746 self.controls.toggleFilterButtons && self._buildToggleArray(); 1747 self._updateControls(args.command, self.controls.toggleFilterButtons); 1748 } 1749 1750 (self._queue.length < 2) && (self._clicking = false); 1751 1752 delete self.callbacks._user; 1753 if(args.callback) self.callbacks._user = args.callback; 1754 1755 var sort = args.command.sort, 1756 filter = args.command.filter, 1757 changeLayout = args.command.changeLayout; 1758 1759 self._refresh(); 1760 1761 if(sort){ 1762 self._newSort = self._parseSort(sort); 1763 self._newSortString = sort; 1764 1765 self._sorting = true; 1766 self._sort(); 1767 } 1768 1769 if(filter !== undf){ 1770 filter = (filter === 'all') ? self.selectors.target : filter; 1771 1772 self._activeFilter = filter; 1773 } 1774 1775 self._filter(); 1776 1777 if(changeLayout){ 1778 self._newDisplay = (typeof changeLayout === 'string') ? changeLayout : changeLayout.display || self.layout.display; 1779 self._newClass = changeLayout.containerClass || ''; 1780 1781 if( 1782 self._newDisplay !== self.layout.display || 1783 self._newClass !== self.layout.containerClass 1784 ){ 1785 self._changingLayout = true; 1786 1787 self._changingClass = (self._newClass !== self.layout.containerClass); 1788 self._changingDisplay = (self._newDisplay !== self.layout.display); 1789 } 1790 } 1791 1792 self._$targets.css(self._brake); 1793 1794 self._goMix(args.animate ^ self.animation.enable ? args.animate : self.animation.enable); 1795 1796 self._execAction('multiMix', 1, arguments); 1797 1798 } else { 1799 if(self.animation.queue && self._queue.length < self.animation.queueLimit){ 1800 self._queue.push(arguments); 1801 1802 (self.controls.enable && !self._clicking) && self._updateControls(args.command); 1803 1804 self._execAction('multiMixQueue', 1, arguments); 1805 1806 } else { 1807 if(typeof self.callbacks.onMixBusy === 'function'){ 1808 self.callbacks.onMixBusy.call(self._domNode, self._state, self); 1809 } 1810 self._$container.trigger('mixBusy', [self._state, self]); 1811 1812 self._execAction('multiMixBusy', 1, arguments); 1813 } 1814 } 1815 }, 1816 1817 /** 1818 * Insert 1819 * @since 2.0.0 1820 * @param {array} arguments 1821 */ 1822 1823 insert: function(){ 1824 var self = this, 1825 args = self._parseInsertArgs(arguments), 1826 callback = (typeof args.callback === 'function') ? args.callback : null, 1827 frag = document.createDocumentFragment(), 1828 target = (function(){ 1829 self._refresh(); 1830 1831 if(self._$targets.length){ 1832 return (args.index < self._$targets.length || !self._$targets.length) ? 1833 self._$targets[args.index] : 1834 self._$targets[self._$targets.length-1].nextElementSibling; 1835 } else { 1836 return self._$parent[0].children[0]; 1837 } 1838 })(); 1839 1840 self._execAction('insert', 0, arguments); 1841 1842 if(args.$object){ 1843 for(var i = 0; i < args.$object.length; i++){ 1844 var el = args.$object[i]; 1845 1846 frag.appendChild(el); 1847 frag.appendChild(document.createTextNode(' ')); 1848 } 1849 1850 self._$parent[0].insertBefore(frag, target); 1851 } 1852 1853 self._execAction('insert', 1, arguments); 1854 1855 if(typeof args.multiMix === 'object'){ 1856 self.multiMix(args.multiMix, callback); 1857 } 1858 }, 1859 1860 /** 1861 * Prepend 1862 * @since 2.0.0 1863 * @param {array} arguments 1864 */ 1865 1866 prepend: function(){ 1867 var self = this, 1868 args = self._parseInsertArgs(arguments); 1869 1870 self.insert(0, args.$object, args.multiMix, args.callback); 1871 }, 1872 1873 /** 1874 * Append 1875 * @since 2.0.0 1876 * @param {array} arguments 1877 */ 1878 1879 append: function(){ 1880 var self = this, 1881 args = self._parseInsertArgs(arguments); 1882 1883 self.insert(self._state.totalTargets, args.$object, args.multiMix, args.callback); 1884 }, 1885 1886 /** 1887 * Get Option 1888 * @since 2.0.0 1889 * @param {string} string 1890 * @return {mixed} value 1891 */ 1892 1893 getOption: function(string){ 1894 var self = this, 1895 getProperty = function(obj, prop){ 1896 var parts = prop.split('.'), 1897 last = parts.pop(), 1898 l = parts.length, 1899 i = 1, 1900 current = parts[0] || prop; 1901 1902 while((obj = obj[current]) && i < l){ 1903 current = parts[i]; 1904 i++; 1905 } 1906 1907 if(obj !== undf){ 1908 return obj[last] !== undf ? obj[last] : obj; 1909 } 1910 }; 1911 1912 return string ? self._execFilter('getOption', getProperty(self, string), arguments) : self; 1913 }, 1914 1915 /** 1916 * Set Options 1917 * @since 2.0.0 1918 * @param {object} config 1919 */ 1920 1921 setOptions: function(config){ 1922 var self = this; 1923 1924 self._execAction('setOptions', 0, arguments); 1925 1926 typeof config === 'object' && $.extend(true, self, config); 1927 1928 self._execAction('setOptions', 1, arguments); 1929 }, 1930 1931 /** 1932 * Get State 1933 * @since 2.0.0 1934 * @return {object} state 1935 */ 1936 1937 getState: function(){ 1938 var self = this; 1939 1940 return self._execFilter('getState', self._state, self); 1941 }, 1942 1943 /** 1944 * Force Refresh 1945 * @since 2.1.2 1946 */ 1947 1948 forceRefresh: function(){ 1949 var self = this; 1950 1951 self._refresh(false, true); 1952 }, 1953 1954 /** 1955 * Destroy 1956 * @since 2.0.0 1957 * @param {boolean} hideAll 1958 */ 1959 1960 destroy: function(hideAll){ 1961 var self = this, 1962 filters = $.MixItUp.prototype._bound._filter, 1963 sorts = $.MixItUp.prototype._bound._sort; 1964 1965 self._execAction('destroy', 0, arguments); 1966 1967 self._$body 1968 .add($(self.selectors.sort)) 1969 .add($(self.selectors.filter)) 1970 .off('.mixItUp'); 1971 1972 for(var i = 0; i < self._$targets.length; i++){ 1973 var target = self._$targets[i]; 1974 1975 hideAll && (target.style.display = ''); 1976 1977 delete target.mixParent; 1978 } 1979 1980 self._execAction('destroy', 1, arguments); 1981 1982 if(filters[self.selectors.filter] && filters[self.selectors.filter] > 1) { 1983 filters[self.selectors.filter]--; 1984 } else if(filters[self.selectors.filter] === 1) { 1985 delete filters[self.selectors.filter]; 1986 } 1987 1988 if(sorts[self.selectors.sort] && sorts[self.selectors.sort] > 1) { 1989 sorts[self.selectors.sort]--; 1990 } else if(sorts[self.selectors.sort] === 1) { 1991 delete sorts[self.selectors.sort]; 1992 } 1993 1994 delete $.MixItUp.prototype._instances[self._id]; 1995 } 1996 1997 }; 1998 1999 /* jQuery Methods 2000 ---------------------------------------------------------------------- */ 2001 2002 /** 2003 * jQuery .mixItUp() method 2004 * @since 2.0.0 2005 * @extends $.fn 2006 */ 2007 2008 $.fn.mixItUp = function(){ 2009 var args = arguments, 2010 dataReturn = [], 2011 eachReturn, 2012 _instantiate = function(domNode, settings){ 2013 var instance = new $.MixItUp(), 2014 rand = function(){ 2015 return ('00000'+(Math.random()*16777216<<0).toString(16)).substr(-6).toUpperCase(); 2016 }; 2017 2018 instance._execAction('_instantiate', 0, arguments); 2019 2020 domNode.id = !domNode.id ? 'MixItUp'+rand() : domNode.id; 2021 2022 if(!instance._instances[domNode.id]){ 2023 instance._instances[domNode.id] = instance; 2024 instance._init(domNode, settings); 2025 } 2026 2027 instance._execAction('_instantiate', 1, arguments); 2028 }; 2029 2030 eachReturn = this.each(function(){ 2031 if(args && typeof args[0] === 'string'){ 2032 var instance = $.MixItUp.prototype._instances[this.id]; 2033 if(args[0] === 'isLoaded'){ 2034 dataReturn.push(instance ? true : false); 2035 } else { 2036 var data = instance[args[0]](args[1], args[2], args[3]); 2037 if(data !== undf)dataReturn.push(data); 2038 } 2039 } else { 2040 _instantiate(this, args[0]); 2041 } 2042 }); 2043 2044 if(dataReturn.length){ 2045 return dataReturn.length > 1 ? dataReturn : dataReturn[0]; 2046 } else { 2047 return eachReturn; 2048 } 2049 }; 2050 2051 /** 2052 * jQuery .removeStyle() method 2053 * @since 2.0.0 2054 * @extends $.fn 2055 */ 2056 2057 $.fn.removeStyle = function(style, prefix){ 2058 prefix = prefix ? prefix : ''; 2059 2060 return this.each(function(){ 2061 var el = this, 2062 styles = style.split(' '); 2063 2064 for(var i = 0; i < styles.length; i++){ 2065 for(var j = 0; j < 4; j++){ 2066 switch (j) { 2067 case 0: 2068 var prop = styles[i]; 2069 break; 2070 case 1: 2071 var prop = $.MixItUp.prototype._helpers._camelCase(prop); 2072 break; 2073 case 2: 2074 var prop = prefix+styles[i]; 2075 break; 2076 case 3: 2077 var prop = $.MixItUp.prototype._helpers._camelCase(prefix+styles[i]); 2078 } 2079 2080 if( 2081 el.style[prop] !== undf && 2082 typeof el.style[prop] !== 'unknown' && 2083 el.style[prop].length > 0 2084 ){ 2085 el.style[prop] = ''; 2086 } 2087 2088 if(!prefix && j === 1)break; 2089 } 2090 } 2091 2092 if(el.attributes && el.attributes.style && el.attributes.style !== undf && el.attributes.style.value === ''){ 2093 el.attributes.removeNamedItem('style'); 2094 } 2095 }); 2096 }; 2097 2098 })(jQuery);